Java 中的泛型类型擦除

Generics type erasure in Java

代码如下:

public class Main {
    public static void main(String[] args) {
        Gen<Integer> g = new Gen<Integer>(5);

        System.out.println(g.getClass());
        System.out.println(g.ob.getClass());
    }
}

class Gen<T> {
    T ob;

    public Gen(T x) {
        ob = x;
    }
}

这是输出

class Gen               // This I understand
class java.lang.Integer // But if type erasure is happening, shouldn't this be java.lang.Object?

我知道类型参数 T 在运行时被删除了,但是为什么 ob 的类型参数在运行时仍然存在?

Type erasure 正在发生。泛型是一个编译时类型检查系统。在 运行 时间你仍然得到 class (它是 运行 时间类型信息)。链接的类型擦除文档说(部分)

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.

您的实例有一个类型,它是 Object。但是 Object 引用可以引用 Java 中的任何子 class(每个 class)。你得到它所指的类型。

不!

考虑一下:

Object x = new Integer(1);
System.out.println(x.toString());

您将获得 1

但我不应该得到 Object.toString() 吗?

没有。虽然 x 是类型 Object 的引用,但实际的引用对象是 Integer,因此在 运行 时,Integer 实现 toString 叫做。

getClass也一样。

无论变量是什么类型,getClass()的return值取决于变量的内容。因此,由于您基本上有一个包含 Integer 的变量 Object ob(您的 int 在您将其作为构造函数的参数提供时已转换为它),[=16 的输出=] 是 class java.lang.Integer.

此外,关于您的问题,即为什么 getClass() 会记住类型参数:它不会。它所做的只是确定内容的class。例如:

class Foo {
    public Foo() {}
}

class Bar extends Foo {
    public Bar() {}
}

class Baz<T> {
    public T object;

    public Baz(T object) { this.object = object; }
}

如果您现在 运行 以下片段...

public static void main(String... args) {
    Baz<Foo> obj = new Baz(new Bar());
    System.out.println(obj.object.getClass());
}

您会注意到输出不是 class Foo,而是 class Bar

因为编译的时候,classGen有一个Object ob;仿制药从最终产品中消失。尖括号仅在编译时的静态类型检查期间起作用。这是编译器可以为您做的事情,让您更加安心,确保您正确使用集合和其他参数化类型。

在运行时分配给 ob 的实际对象是 Integer class 的实例,而 ob.getClass() 用于找出引用对象的实际 class通过指针 -> 因此你会看到 java.lang.Integer 打印出来。

记住,出来的是有效的class Gen { Object ob; ...}

自从我第一次接触泛型是使用 C# 以来,我花了一些时间才弄明白 java.

中的擦除类型是什么

但是在更好地理解 java 泛型之后,我意识到在我的问题中我混合了 2 个不同的主题:泛型和反射。

主要问题是,为什么第二个电话打到这里

 System.out.println(g.getClass());
 System.out.println(g.ob.getClass());

returned java.lang.Integer 而不是 java.lang.Object.

查看 getClass() 的文档,答案显而易见

Returns the runtime class of this Object.

所以,getClass() 不是 return 引用的类型,而是引用所引用的实际对象。

例如:

Object o =  "abc";
System.out.println(o.getClass());

输出不会是引用的类型java.lang.Object,而是对象的实际类型java.lang.String