简体   繁体   English

Java中的泛型类型擦除

[英]Generics type erasure in Java

Here's the code: 这是代码:

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;
    }
}

And here's the output 这是输出

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

I get it that Type parameter T is erased at runtime, but then why is the type parameter of ob surviving at runtime? 我知道在运行时擦除了Type参数T ,但是为什么ob的类型参数在运行时存活?

Nope! 不!

Consider this: 考虑一下:

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

You'll get 1 . 你会得到1

But shouldn't I get Object.toString()? 但是我不应该得到Object.toString()?

No. While x is a reference of type Object , the actual referent is an Integer , so at run-time, the Integer implementation of toString is called. 虽然xObject类型的引用,但实际的引用是一个Integer ,因此在运行时,调用toStringInteger实现。

It is the same for getClass . getClass也是一样。

Type erasure is happening. 类型擦除正在发生。 Generics are a compile time type checking system. 泛型是一个编译时类型检查系统。 At run-time you still get the class (it is run-time type information). 在运行时,您仍然可以获得该类(它是运行时类型信息)。 The linked Type erasure documentation says (in part) 链接的类型擦除文档说(部分)

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. 泛型被引入到Java语言中,以便在编译时提供更严格的类型检查并支持泛型编程。 To implement generics, the Java compiler applies type erasure to: 为了实现泛型,Java编译器将类型擦除应用于:

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. 因此,生成的字节码仅包含普通的类,接口和方法。

Your instance has a type, it's Object . 您的实例有一个类型,它是Object But an Object reference can refer to any sub-class (which is every class) in Java. 但是Object引用可以引用Java中的任何子类(每个类)。 You get the type it refers to. 你得到它所指的类型。

No matter what type the variable has, the return value of getClass() depends on the variable's contents . 无论变量具有什么类型, getClass()的返回值都取决于变量的内容 So, since you basically have a variable Object ob which contains an Integer (your int was converted to it at the time you provided it as that constructor's parameter), the output of ob.getClass() is class java.lang.Integer . 因此,由于您基本上有一个包含Integer的变量Object ob (在您将其作为构造函数的参数提供时将int转换为它), ob.getClass()的输出是class java.lang.Integer

Also, about your question as to why getClass() remembers the type argument: it doesn't. 另外,关于为什么getClass()记住类型参数的问题:它没有。 All it does is determine the content's class. 它所做的只是确定内容的类。 For example: 例如:

class Foo {
    public Foo() {}
}

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

class Baz<T> {
    public T object;

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

If you now run the following snippet... 如果您现在运行以下代码段...

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

You will notice that the output is not class Foo , it's class Bar . 你会注意到输出不是class Foo ,它是class Bar

Because when compiled, class Gen has an Object ob; 因为在编译时,类Gen有一个Object ob; The generics disappear from the final product. 仿制药从最终产品中消失。 The angle brackets only play a role at compile-time, during static type checking. 在静态类型检查期间,尖括号仅在编译时起作用。 It's something the compiler can do for you to give you better peace of mind, to assure you that you're using collections and other paramterized types correctly. 这是编译器可以为您做的事情,让您更安心,确保您正确使用集合和其他参数化类型。

the actual object assigned to ob at runtime is an instance of the Integer class, and ob.getClass() serves the purpose to find out the actual class of the object referenced by the pointer -> hence you will see java.lang.Integer printed. 在运行时分配给ob的实际对象是Integer类的实例,ob.getClass()用于查找指针引用的对象的实际类 - >因此您将看到打印的java.lang.Integer 。

Remember, what comes out is effectively class Gen { Object ob; 记住,出来的是有效的类Gen {Object ob; ... } ...}

Since my first exposure to generics was with C# , it took time get a hold of what type erasure is in java. 由于我第一次接触泛型是使用C#,所以需要时间来掌握java中的类型擦除。

But after better understanding java generics , i realized that in my question i'm mixing 2 separate topics : Generics and Reflection. 但是在更好地理解java泛型后,我意识到在我的问题中我混合了两个独立的主题:泛型和反射。

The main question was , why did the second call here 主要问题是,为什么第二次打电话给这里

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

returned java.lang.Integer instead of java.lang.Object . 返回java.lang.Integer而不是java.lang.Object

Looking at the docs for getClass() , the answer becomes obvious 查看getClass()的文档,答案显而易见

Returns the runtime class of this Object. 返回此Object的运行时类。

so, getClass() doesn't return the type of reference but the actual object being referred to by the reference. 因此, getClass()不返回引用类型,而是引用引用的实际对象。

For ex : 例如:

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

The output wouldn't be the type of the reference java.lang.Object but rather the actual type of the object java.lang.String . 输出不是引用java.lang.Object的类型,而是对象java.lang.String实际类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM