英文:
Generics in Java difference from non generics in Java
问题
所以,普通类、方法与泛型方法或类的区别在于类或方法定义的构造函数可以接受任何类型的泛型参数值?我也阅读过关于这个问题,但不太确定,关于泛型参数值如何在这个称为类型擦除的概念中最终编译为相同的数据类型?
另外,如果我像这样创建了两个实例:
Test<String> sObj = new Test<String>("GeeksForGeeks");
Test<Integer> sObj2 = new Test<Integer>(4);
假设Test
是一个泛型类,我理解这两者是Test
类的两个不同实例,但我能否说这两者是Test
类的两种不同类型,因为它们都使用了不同的泛型参数?或者说,如果它们最终通过类型擦除编译为相同的内容,我能否安全地说它们都是相同类型的Test
类对象?
英文:
So what makes a normal class, method vs a generic method or class is that the constructor for the class or method definition can take any type of argument value for Generics? I also read about and not sure about this but how all of the Generic argument values end up compiling to the same data type somehow in this concept called Type Erasure?
Also, if I did two instances like this
Test <String> sObj = new Test<String>("GeeksForGeeks");
Test <Integer> sObj2 = new Test<Integer>(4);
Assuming Test is a Generic class, I understand both are two different instances of Test class, but could I say these are two different types of Test class since both use two different Generic parameters, or would it be safe to say they both are same type of Test class objects if they end up compiling the same through Type Erasure I believe?
答案1
得分: 1
在这里有两个不同的上下文要考虑:编译时和运行时。
在编译时,泛型和非泛型类的区别在于类或方法的签名中是否存在一个或多个泛型参数。
在编译时,Test<String>
和 Test<Integer>
被视为不同的类型。这样做的好处是,如果您尝试在需要 Test<Integer>
的地方使用 Test<String>
,编译器会报错。
sObj = sObj2; // 编译器报错
这是很好的,因为这可能是一个简单的错误,您希望立即捕捉并修复它。
在运行时,每个对象都与一些额外的元数据关联,以告诉您它的类型是什么。这对许多方面都很有帮助,包括反射(调用像 .getClass()
这样的方法)和防止无效的强制类型转换:
Object obj = "foo";
Integer i = (Integer) obj; // 编译器不会报错,但会抛出异常
类型擦除意味着这些额外的元数据在运行时不包含关于泛型类型的任何信息。
if (sObj.getClass().equals(sObj2.getClass())) { // 这将为真
sObj = (Test<String>) sObj1; // 这不会产生错误。
}
这也有一些缺点。例如,上面的代码显然存在逻辑错误,但是您不会在上面的行中看到无效的强制类型转换错误,而是在代码的完全不同部分尝试将 Integer
转换为 String
时可能会出现无效的强制类型转换错误。
但是到了Java语言添加泛型的时候,以任何其他方式实现它都会在努力方面和向后兼容性损失方面有很多其他缺点。因此,他们选择使用类型擦除而不是具体化泛型。
另请参阅:https://stackoverflow.com/q/1927789/120955
英文:
There are two different contexts to consider here: compile-time and run-time.
The difference between generic and non-generic classes at compile-time is the presence of one or more generic arguments in the signature of the class or method.
At compile time, Test<String>
and Test<Integer>
are treated as different types. The benefit of this is that if you try to use a Test<String>
someplace where a Test<Integer>
is expected, the compiler will complain.
sObj = sObj2; // compiler complains
This is good, because it was probably a simple mistake, which you want to catch and fix right away.
At run-time, each object has a little extra metadata associated with it to tell you what type it is. This is helpful for a lot of reasons, including Reflection (calling methods like .getClass()
) and preventing invalid casts:
Object obj = "foo";
Integer i = (Integer) obj; // compiler doesn't complain, but an exception is thrown
Type Erasure means that this extra little bit of metadata contains no information about generic types at run time.
if(sObj.getClass().equals(sObj2.getClass())) { // This will be true
sObj = (Test<String>) sObj1; // this will not produce an error.
}
There are some down-sides to this. For example, the code above is clearly where there's an error in the logic, but instead of seeing an invalid cast in the line above, you'll probably end up getting an invalid cast in a completely different part of code when you try to convert an Integer
to a String
.
But by the time the Java language added generics, implementing it any other way would have had a lot of other draw-backs in terms of effort and loss of backwards-compatibility. So they chose to use Type Erasure instead of Reified Generics.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论