【问题标题】:Create generic instance inside a method在方法中创建泛型实例
【发布时间】:2016-06-10 11:32:00
【问题描述】:

我用这样的通用返回类型调用一个方法:

public static <T extends HotelBean> List<T> fill(Class<T> c) throws InstantiationException, IllegalAccessException {
    List<T> list = new ArrayList<>();
    for (int i = 0; i <= 2; i++) {
        T t = c.newInstance();
        list.add(t);
    }
    return list;
}

有没有办法在这个方法中创建一个实例而不给出Class 作为方法参数? (Class&lt;T&gt; c)

【问题讨论】:

  • 由于类型擦除,可能不在这样的方法中。编译器也无法推断T 的实际类型。摆脱参数的唯一方法是在某些反射数据中定义T 的类型,即具有具体类型的字段定义或类。但是,这可能会与您的方法的目的相矛盾,或者至少不会比传递参数更容易。
  • 不,泛型类型已被删除 (docs.oracle.com/javase/tutorial/java/generics/erasure.html),因此 Java 可以在运行时使用以确定它应该创建的实例类型的唯一内容来自 Class
  • 您也可以通过 Supplier&lt;T&gt; 来代替,使用 MyType::new 方法引用作为参数。这至少会摆脱 throws 子句
  • @JornVernee 你能提供一个使用Supplier&lt;T&gt; 的例子吗?我对您的解决方案非常感兴趣。
  • @Patrick 我会回答的

标签: java generics methods


【解决方案1】:

不,很遗憾,没有。这是首先必须通过Class&lt;T&gt; 的主要原因。

Java 编译器在称为类型擦除的过程中从已编译的类和方法中去除通用信息。到编译器完成时,所有Ts 都变为Objects 或下限类型(这将对应于您的示例中的HotelBean)。如果无法访问Class&lt;T&gt;,该方法无法恢复类型参数,这是生成新实例所必需的。

请注意,您的示例必须以一种或另一种方式传递所需的类型,因为您正在使用可能是 HotelBean 子类的对象填充基类对象列表。

【讨论】:

  • 感谢您的回答。有没有更好的实施方式或者我应该继续使用这种方法吗?
  • 即使没有类型擦除,编译器也无法推断 T 的类型,因此您必须使用 HotelBean 这会使泛型变得多余或告诉方法,例如像this.&lt;SpecialHotelBean&gt;fill() 这样称呼它——这似乎比fill( SpecialHotelBean.class ) 更可取。
  • @Patrick 我不会尝试发明不同的方法,因为您所拥有的是用于构建对象创建通用代码的惯用解决方案。阅读您的代码的其他程序员会立即认出它,并了解您要做什么。
  • @Thomas 这很有道理,感谢您的评论。尽管在某些情况下编译器可以推断出类型,例如,当您分配给List&lt;SpecialHotelBean&gt; 或调用采用List&lt;SpecialHotelBean&gt; 参数的方法时,必须提供精确的类型才能使方法工作。跨度>
  • 是的,这些是由于类型擦除而中断的情况。
【解决方案2】:

您可以传递 Supplier&lt;T&gt; 来代替传递 Class 来创建实例,这意味着必须处理捕获或抛出异常。

java.util.function.Supplier&lt;T&gt;,(Java 8 的一个特性)是一个函数式接口,它有一个方法get,它返回一个T 类型的对象。

使用Supplier&lt;T&gt; 看起来像这样:

public static <T extends HotelBean> List<T> fill(Supplier<T> supp) { // No more exceptions!
    List<T> list = new ArrayList<>();
    for (int i = 0; i <= 2; i++) {
        T t = supp.get();
        list.add(t);
    }
    return list;
}

您可以传递不带参数的方法引用并返回类型为T 的对象作为供应商,其中包括T 的默认构造函数。

class MyClass {
    public MyClass() {...}
}
fill(MyClass::new);

newInstance() 的主要区别在于,从Supplier&lt;T&gt; 返回的对象不一定是new 对象,因为您不必一定要传递MyClass::new。您可以传递任何不带参数并返回 T 的方法。

【讨论】:

    猜你喜欢
    • 2011-05-19
    • 1970-01-01
    • 2013-02-17
    • 2020-04-03
    • 2011-03-17
    • 2010-09-09
    • 2014-05-31
    • 1970-01-01
    相关资源
    最近更新 更多