Java:没有默认构造函数的类的新实例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3671649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 03:20:59  来源:igfitidea点击:

Java: newInstance of class that has no default constructor

javareflectionconstructornew-operatorinstance

提问by GermanK

I'm trying to build an automatic testing framework (based on jUnit, but that's no important) for my students' homework. They will have to create constructors for some classes and also add some methods to them. Later, with the testing functions I provide, they will check if they went alright.

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

What I want to do is, by reflection, create a new instance of some class I want to test. The problem is that, sometimes, there is no default constructor. I don't care about that, I want to create an instance and initialize the instance variables myself. Is there any way of doing this? I'm sorry if this has been asked before, but just I couldn't find any answer.

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

Thanks in advance.

提前致谢。

采纳答案by Jon Skeet

Call Class.getConstructor()and then Constructor.newInstance()passing in the appropriate arguments. Sample code:

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

回答by Dani Cricco

You can use Class.getConstructoror Class.getConstructorsand then use the method Constructor.newInstanceto initialize the object that you want to use.

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

回答by Bill K

If you haven't used mocking frameworks (like ezmock) I highly recommend you give one a try.

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

I may be wrong and that may not help you at all, but from what I could gather from your post it seems possible that mocking may be exactly what you are looking for (Even though I recognize that it has nothing to do with what you askedfor.

我可能是错的,这可能对你没有任何帮助,但从我从你的帖子中收集到的信息看来,嘲笑可能正是你想要的(尽管我认识到它与你问的无关为了。

Edit: In response to comment.

编辑:回应评论。

No, Modern mocking frameworks allow you to create a "Fake" instance of any class from "nothing" and pass it around as though it was an instance of the class. It doesn't need an interface, it can be any class. Also methods can be scripted to return a sequence of values from a simple always return "7" to "When called with an arg=7 return 5 the first call, 6 the second and 7 the third".

不,现代模拟框架允许您从“无”创建任何类的“假”实例,并将其作为类的实例传递。它不需要接口,它可以是任何类。还可以编写方法来返回一系列值,从简单的始终返回“7”到“当使用 arg=7 调用时,返回 5 第一次调用,6 次第二次调用和 7 次第三次调用”。

It's usually used in conjunction with testing frameworks to give a reference class to pass to the class you are testing.

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

This may not be exactly what you are looking for, but you mentioned unit testing and manually initializing variables so it seemed like this is something that may eventually come in handy.

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

回答by emory

You could distribute the following source code with your assignment. Tell the students to include it in their source code. Their code won't compile unless they code an Assignment class with the proper signature. The compiler does the signaure checking for you.

您可以随作业分发以下源代码。告诉学生将它包含在他们的源代码中。他们的代码不会编译,除非他们用正确的签名编写一个 Assignment 类。编译器会为您进行签名检查。

Then your testing program does not need to use reflection. Just instantiate an AssignmentFactory and call the make method with the proper arguments.

那么你的测试程序就不需要使用反射了。只需实例化一个 AssignmentFactory 并使用适当的参数调用 make 方法。

If you use this idea, your new challenge will be some students modifying AssignmentFactory to fit their Assignment class (breaking your testing program).

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

package assignment ;

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

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

回答by Vlad

Here is a generic solution that does not require javassist or another bytecode "manipulator". Although, it assumes that constructors are not doing anything else than simply assigning arguments to corresponding fields, so it simply picks the first constructor and creates an instance with default values (i.e. 0 for int, null for Object etc.).

这是一个不需要 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. Works with Java 1.5+. The solution also assumes there's no SecurityManager manager that could prevent invocation of f.setAccessible(true).

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

回答by PaperBeatsRock-PFFFT

I used the following code to create a list of generic objects of any type of class name passed in. It uses all of the set methods within the class to set all the values passed in via the result set. I'm posting this in case anyone was interested in it as well.

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