4

背景

我对Java 泛型的理解是它完全是一个编译时特性(主要关注类型安全检查)。任何泛型类的类型信息都会在运行时丢失(类型擦除)。

尽管如此,我还是看到许多框架似乎也在运行时利用了类型信息。例如,谷歌 guice提供者。guice 提供者可以在运行时实例化并提供其泛型类型的新实例。

class Container
{
     @Inject   
     public Container(Provider<Content> contentProvider)
     {
     //This works at Runtime... but How ??? 
     //When type's are not even preserved at runtime, how does the Provider knows it has to instantiate an object of type 'Content' 
     Content content = contentProvider.get();   
     }
}

问题

  1. 是否有任何与泛型类型相关的信息也在运行时保留。? 如果 ,是什么?如果 没有,那么像 google guice 这样的库如何在内部运行(上例)

  2. 泛型不仅仅是编译时安全吗?例如,是否有任何用例(除了确保编译时安全)可以使用泛型获得优势?

4

6 回答 6

3

如果一个类扩展了一个泛型类或接口并为参数提供了一个具体类型,那么该类型可以通过Class.getGenericSuperclass(). 该方法将(在这种情况下)返回ParameterizedType包含实际参数化的 a。

例如,如果您有:

class BigIntegerList extends ArrayList<BigInteger> {}

然后你可以这样做:

Class<BigIntegerList> fooClass = BigIntegerList.class;
Type superclass = fooClass.getGenericSuperclass();
if (superclass instanceof ParameterizedType) {
  ParameterizedType parameterized = (ParameterizedType) superclass;
  Type[] parameterizations = parameterized.getActualTypeArguments();
  System.out.println(Arrays.toString(parameterizations));
  // prints: "[class java.math.BigInteger]"
}

这确实被 Guice 等重度反射库使用。另一个例子是 Jackson's TypeReference,它可以让您将 JSON 列表读取为 BigDecimal 列表(例如)。

于 2017-08-21T18:06:00.873 回答
3

当然,支持类通用的信息。

换句话说:当你反编译 ArrayList.class 时,你会发现这个类允许一个泛型类型参数这一事实的提示。换句话说:类文件包含信息。并且使用反射可以在运行时检查此元信息。

但是,当您有另一个使用某个List<Integer>对象的类时——那么你在编译的类中找不到关于“列表使用整数”的信息——除非你使用一些特定的模式,例如这里概述的。

所以答案基本上是:对于几乎所有实际相关的用例,“泛型”只是编译时间

例子:

public class GenericsExample<T> {
  private T member;   
  public T foo(T bar) {
     return member;
  }
}

现在运行: javap -p -c GenericsExample

Compiled from "GenericsExample.java"
public class GenericsExample<T> {
  private T member;

  public GenericsExample();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public T foo(T);
    Code:
       0: aload_0       
       1: getfield      #2                  // Field member:Ljava/lang/Object;
       4: areturn       
}

如您所见,反编译器理解该类使用该泛型类型 T。有关更多详细信息,请参见此处此处

于 2017-08-21T18:32:34.300 回答
0

如果您不知道要为特定类使用什么类型,泛型是一种很好的编程方式。在运行时,将根据类的输入设置泛型类类型。它主要用于编译时安全。

于 2017-08-21T17:49:16.697 回答
0
  1. Java 泛型使用称为类型擦除的东西,因此在运行时没有关于类型的信息可用。但是,Class.newInstance()如果可以以某种方式传递类型信息,则可以使用方法创建任何类的实例(事实上,这是创建泛型数组的唯一方法)

  2. 编译时安全是泛型的主要目标。但是它们通常也可以用来编写更简洁的代码,否则这是不可能的

对于详细的处理,我推荐优秀的书Java Generics and Collections

于 2017-08-21T17:58:28.460 回答
0

是否有任何与泛型类型相关的信息也在运行时保留。? 如果是,是什么?

保存在编译类中的单个信息是从擦除后获得的原始对象/变量转换为在源代码中用作泛型的特定类型。
但是这些只依赖于声明的变量类型,而不是使用的真正的泛型类型。

因此,在运行时,您无法在没有解决方法的情况下直接访问泛型信息,因为在实例化泛型类时传递了一个类。

如果不是,那么像 google guice 这样的库如何在内部运行(

你错了。
在 Guice 中,这段代码:

 Content content = contentProvider.get();   

将返回 的实例Content,而不是泛型类型。查看文档

得到()

提供 T 的一个实例。

于 2017-08-21T17:59:25.030 回答
-2

通用信息仅持续到编译时间。在您的示例中,它也可用到编译时间。让我用一个例子来解释一下

公共无效打印对象(列表空){

// 这不显示该列表在运行时保留此信息,即它将返回什么类型的对象。就业 en =empt.get(); }

于 2017-08-21T18:12:21.693 回答