繁体   English   中英

Java:没有默认构造函数的类的newInstance

[英]Java: newInstance of class that has no default constructor

我正在尝试为学生的作业构建一个自动测试框架(基于jUnit,但这并不重要)。 他们必须为某些类创建构造函数,并为它们添加一些方法。 后来,通过我提供的测试功能,他们将检查它们是否正常。

我想做的是, 通过反射 ,创建一个我想要测试的类的新实例。 问题是,有时候, 没有默认的构造函数 我不关心这个, 我想创建一个实例并自己初始化实例变量 有没有办法做到这一点? 如果以前曾经问过这个问题我很抱歉,但我找不到任何答案。

提前致谢。

调用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);           
    }
}

这是一个通用的解决方案,不需要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;
}

PS适用于Java 1.5+。 该解决方案还假设没有SecurityManager管理器可以阻止调用f.setAccessible(true)

如果您还没有使用过模拟框架(比如ezmock),我强烈建议您尝试一下。

我可能是错的,这可能对你没什么帮助,但是从你的帖子中我可以收集到的东西似乎可能是你正在寻找的嘲讽(即使我认识到它与你所要求的无关对于。

编辑:回应评论。

不,现代模拟框架允许您从“无”创建任何类的“假”实例并将其传递,就好像它是类的实例一样。 它不需要接口,它可以是任何类。 此外,方法可以编写脚本以返回一个值序列,从简单的总是返回“7”到“当用arg = 7调用时返回5第一个调用,6返回第一个调用,7调用第三个调用”。

它通常与测试框架结合使用,以提供一个引用类来传递给您正在测试的类。

这可能不是您正在寻找的,但您提到了单元测试并手动初始化变量,因此看起来这可能最终会派上用场。

我使用以下代码创建了传入的任何类型名称的泛型对象列表。它使用类中的所有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;
    }

您可以使用Class.getConstructorClass.getConstructors ,然后使用方法Constructor.newInstance初始化您要使用的对象。

您可以在作业中分发以下源代码。 告诉学生将其包含在源代码中。 除非他们使用适当的签名编写Assignment类,否则他们的代码将无法编译。 编译器会为您进行签名检查。

然后你的测试程序不需要使用反射。 只需实例化一个AssignmentFactory并使用正确的参数调用make方法。

如果您使用这个想法,您的新挑战将是一些学生修改AssignmentFactory以适合他们的Assignment类(打破您的测试程序)。

package assignment ;

public class AssignmentFactory
{
     public AssignmentFactory ( )
     {
           super ( ) ;
     }

     public AssignmentFactory make ( .... parameters )
     {
           return new Assignment ( .... arguments ) ;
     }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM