【问题标题】:Instantiate object of given class by invoking implicit default constructor通过调用隐式默认构造函数来实例化给定类的对象
【发布时间】:2021-11-26 18:55:37
【问题描述】:

目前,我正在尝试实例化一个类的对象,该类作为参数给出,并使用默认值(例如 null)初始化对象的属性。问题是,我的类没有任何构造函数,并且由于各种原因我无法添加一个。可能的类也不由超类连接,它们都是 Object 的直接子类。我尝试实例化的类如下所示:

public class MyClass {
  public String prop1;

  public int prop2;

  public int prop3;
}

这意味着类只包含属性而没有方法,因此没有显式的构造函数。如果我事先知道课程,我当然可以这样做:

public MyClass create() {
  return new MyClass();
}

在上述情况下,编译器为我生成了 MyClass 的默认构造函数,并使用其默认值初始化所有属性。正如 cmets 中所指出的,这意味着我在技术上拥有一个构造函数,但我只能使用“new”关键字并事先了解类来访问它。现在,我想做的(基本上)如下:

public someClass create(Class<?> someClass) {
  return new someClass();
}

当然,'new' 关键字在这种情况下不起作用,所以我尝试了许多替代方案,但目前都没有一个有效的方法:

  1. 使用someClass.getConstructor().newInstance()someClass.getDeclaredConstructor().newInstance()。代码已编译,但我得到了预期的“NoSuchMethodException”,因为它找不到 -someClass 的方法或构造函数
  2. 从像 Object newObject = someClass.cast(new Object()) 这样的对象投射。正如预期的那样,这也会崩溃,因为您无法从超类转换为子类

我现在唯一真正的选择是,因为可能的类的范围不是太大,所以为每个类使用“instanceof”s,然后在每个分支中使用“new”关键字来变出一个巨大的 switch-case .但正如你可以想象的那样,我不太喜欢那个。

那么我是在尝试做不可能的事情还是有办法做到这一点?

解决方案: 原来第一种方法是完全有效的,只是因为我的一些类是数组而无法正常工作,而我没有意识到这一点。如果我采用数组中对象的实际类,它就可以工作。很抱歉造成混乱,这一直是我的错误!

【问题讨论】:

  • 我认为少量的instanceof 检查没有任何问题。不是很可扩展,但编译时安全,不使用反射。 UPD:抱歉,不是instanceof,而是someClass == SomeClassSomeClass.class.isAssignableFrom(someClass),具体取决于您的需要。
  • “我的类没有任何构造函数” 这是不可能的:如果您的代码没有提供构造函数,则会为您生成一个默认的无参数构造函数。请提供一个minimal reproducible example 来说明问题。
  • @MarkRotteveel 这正是我的问题。我需要一种方法来访问这个“默认的无参数构造函数”或类似的东西,但对于一个在编译时未知的类。
  • 你总是至少有一个构造函数。如果您没有明确说明,则有一个从超类派生的隐式无参数构造函数的无参数构造函数,其行为就像定义为 public MyClass() { super(); } 一样。

标签: java class object generics reflection


【解决方案1】:

方法Class::getConstructor返回公共构造函数,因此它可能无法检测到非公共无参数构造函数:

返回一个 Constructor 对象,该对象反映此 Class 对象表示的类的指定公共构造函数。 parameterTypes 参数是一个 Class 对象数组,它们按照声明的顺序标识构造函数的形式参数类型。如果此 Class 对象表示在非静态上下文中声明的内部类,则形式参数类型包括显式封闭实例作为第一个参数。

要反映的构造函数是此 Class 对象表示的类的公共构造函数,其形参类型与 parameterTypes 指定的类型匹配。

因此,应该定义并实现方法create(Class&lt;?&gt; someClass) 以调用Class::newInstance,但是,由于Java 9 已被弃用,因此应将其替换为someClass.getDeclaredConstructor().newInstance()

此外,检查的异常可能会被捕获并包装到 RuntimeException

public static <T> T create(Class<T> someClass) {
    try {
        return someClass.getDeclaredConstructor().newInstance();
    } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
        throw new RuntimeException("Failed to create instance of class " + someClass.getName(), ex);
    }
}

测试:

MyClass mc = create(MyClass.class);

System.out.println(mc); // overridden toString

输出

MyClass :: prop1 = null; prop2 = 0; prop3 = 0

【讨论】:

  • 这假设我事先知道该类,因为您手动将“MyClass.class”作为参数传递,而我不知道。在我的情况下,我使用“getType()”从 Field 对象中动态检索类,因此我无权访问“.class”引用
  • Field::getType 返回Class&lt;?&gt;,您应该可以将其进一步传递给方法create
  • 你完全正确!!!它只是不起作用,因为我的一些课程是数组,我没有注意到。但是再次尝试您的解决方案使我意识到这一点,所以谢谢!
  • 对,数组可以用java.lang.reflect.Array.newInstance(componentType, length)创建。
猜你喜欢
  • 2015-01-24
  • 1970-01-01
  • 1970-01-01
  • 2016-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多