我正在尝试为学生的作业构建一个自动测试框架(基于jUnit,但这并不重要)。他们必须为某些类创建构造函数,并为它们添加一些方法。后来,通过我提供的测试功能,他们将检查它们是否正常。
我想要做的是,通过反射,创建一个我想要测试的类的新实例。问题是,有时,没有默认构造函数。我不关心,我想创建一个实例并自己初始化实例变量。有没有办法做到这一点? 如果以前曾经问过这个问题我很抱歉,但我找不到任何答案。
提前致谢。
答案 0 :(得分:48)
调用Class.getConstructor()
然后Constructor.newInstance()
传入适当的参数。示例代码:
import java.lang.reflect.*;
public class Test {
public Test(int x) {
System.out.println("Constuctor called! x = " + x);
}
// Don't just declare "throws Exception" in real code!
public static void main(String[] args) throws Exception {
Class<Test> clazz = Test.class;
Constructor<Test> ctor = clazz.getConstructor(int.class);
Test instance = ctor.newInstance(5);
}
}
答案 1 :(得分:6)
这是一个通用的解决方案,不需要javassist或其他字节码“操纵器”。虽然,它假设构造函数除了简单地将参数分配给相应的字段之外没有做任何其他操作,因此它只选择第一个构造函数并创建一个具有默认值的实例(即0表示int,null表示Object等。)
private <T> T instantiate(Class<T> cls, Map<String, ? extends Object> args) throws Exception
{
// Create instance of the given class
final Constructor<T> constr = (Constructor<T>) cls.getConstructors()[0];
final List<Object> params = new ArrayList<Object>();
for (Class<?> pType : constr.getParameterTypes())
{
params.add((pType.isPrimitive()) ? ClassUtils.primitiveToWrapper(pType).newInstance() : null);
}
final T instance = constr.newInstance(params.toArray());
// Set separate fields
for (Map.Entry<String, ? extends Object> arg : args.entrySet()) {
Field f = cls.getDeclaredField(arg.getKey());
f.setAccessible(true);
f.set(instance, arg.getValue());
}
return instance;
}
P.S。适用于Java 1.5+。该解决方案还假设没有可能阻止调用f.setAccessible(true)
的SecurityManager管理器。
答案 2 :(得分:2)
如果你没有使用过模拟框架(比如ezmock),我强烈建议你试一试。
我可能错了,这可能对你没什么帮助,但是从你的帖子里我可以收集到的内容似乎可能是你正在寻找的嘲讽(即使我认识到它与你无关您要求的内容。
编辑:回应评论。
不,现代模拟框架允许您从“无”创建任何类的“假”实例并将其传递,就像它是类的实例一样。它不需要接口,它可以是任何类。此外,方法可以编写脚本以返回一个值序列,从简单的总是返回“7”到“当用arg = 7调用时返回5第一个调用,6返回第一个调用,7个调用第三个”。
它通常与测试框架一起使用,以提供一个引用类传递给您正在测试的类。
这可能不是您正在寻找的,但您提到了单元测试并手动初始化变量,因此看起来这可能最终会派上用场。
答案 3 :(得分:1)
我使用以下代码创建了传入的任何类型名称的泛型对象列表。它使用类中的所有set方法来设置通过结果集传入的所有值。如果有人对它感兴趣的话,我会张贴这个。
protected List<Object> FillObject(ResultSet rs, String className)
{
List<Object> dList = new ArrayList<Object>();
try
{
ClassLoader classLoader = GenericModel.class.getClassLoader();
while (rs.next())
{
Class reflectionClass = classLoader.loadClass("models." + className);
Object objectClass = reflectionClass.newInstance();
Method[] methods = reflectionClass.getMethods();
for(Method method: methods)
{
if (method.getName().indexOf("set") > -1)
{
Class[] parameterTypes = method.getParameterTypes();
for(Class pT: parameterTypes)
{
Method setMethod = reflectionClass.getMethod(method.getName(), pT);
switch(pT.getName())
{
case "int":
int intValue = rs.getInt(method.getName().replace("set", ""));
setMethod.invoke(objectClass, intValue);
break;
case "java.util.Date":
Date dateValue = rs.getDate(method.getName().replace("set", ""));
setMethod.invoke(objectClass, dateValue);
break;
case "boolean":
boolean boolValue = rs.getBoolean(method.getName().replace("set", ""));
setMethod.invoke(objectClass, boolValue);
break;
default:
String stringValue = rs.getString(method.getName().replace("set", ""));
setMethod.invoke(objectClass, stringValue);
break;
}
}
}
}
dList.add(objectClass);
}
}
catch (Exception e)
{
this.setConnectionMessage("ERROR: reflection class loading: " + e.getMessage());
}
return dList;
}
答案 4 :(得分:0)
您可以使用Class.getConstructor或Class.getConstructors,然后使用方法Constructor.newInstance初始化您要使用的对象。
答案 5 :(得分:0)
您可以在作业中分发以下源代码。告诉学生将其包含在源代码中。除非他们使用适当的签名编写Assignment类,否则他们的代码将无法编译。编译器会为您进行签名检查。
然后您的测试程序不需要使用反射。只需实例化一个AssignmentFactory并使用正确的参数调用make方法。
如果你使用这个想法,你的新挑战将是一些学生修改AssignmentFactory以适应他们的Assignment类(打破你的测试程序)。
package assignment ;
public class AssignmentFactory
{
public AssignmentFactory ( )
{
super ( ) ;
}
public AssignmentFactory make ( .... parameters )
{
return new Assignment ( .... arguments ) ;
}
}