Java 泛型:关于泛型类型的任何元信息是否也在运行时保留?

Java Generics : Is any meta information about the generic type preserved at runtime as well?

背景

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

不过,我看到许多框架 似乎 也在运行时利用类型信息。例如,google guice Providers。 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. 是否有与在运行时也保留的泛型类型相关的任何信息。 ? 如果 ,什么?。 If no,那么像 google guice 这样的库如何在内部运行(以上示例)

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

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

  1. Java泛型使用了一种叫做类型擦除的东西,所以没有信息 关于类型在运行时可用。但是,可以 如果可以某种方式传递类型信息,则使用 Class.newInstance() 方法创建任何 class 的实例(事实上,这是可以创建通用数组的唯一方法)

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

详细治疗,推荐好书Java Generics and Collections

Is there any information related to generic types which is preserved at runtime as well. ? If yes, what ?.

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

因此,在运行时,如果没有变通方法,则无法在运行时直接访问通用信息,例如在实例化通用 class.

时传递 class

If no, than how does libraries like google guice operate internally (

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

 Content content = contentProvider.get();   

将 return 是 Content 的实例,而不是通用类型。 看看 documentation :

T get()

Provides an instance of T.

如果 class 扩展通用 class 或接口并为参数提供具体类型,则该类型可通过包含实际参数化的 Class.getGenericSuperclass(). That method will (in this case) return a ParameterizedType 获得。

例如,如果您有:

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 的 TypeReference,它可以让您将 JSON 事物列表读取为 list-of-BigDecimal(例如)。

通用信息只保留到编译时。在您的示例中,它在编译时也是可用的。让我用一个例子来解释它

Public void printObject( list empt) {

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

当然支持class泛型的信息。

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

但是当你有另一个 class 使用一些 List<Integer> 对象时 - 然后你在编译的 class 中找不到关于那个 "list uses an Integer" 的信息 - 除非你使用一些特定的模式,例如概述 here

所以答案基本上是:对于几乎所有与实际相关的用例,"generics" 只是 编译时

示例:

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       
}

如您所见,反编译器理解 class 使用泛型类型 T。有关详细信息,请参阅 or there