[英]Java generics, type erasure and type of a generic member
Java has type erasure and people say that one can't determine the type of a generic object at runtime without hacks. Java有类型擦除,人们说在运行时无法确定通用对象的类型而没有黑客攻击。 Consider the code below
请考虑以下代码
public class TestClass<T> {
private T genericField;
public TestClass(T genericField) {
this.genericField = genericField;
}
public void printTypeInfo() {
System.out.println("Hi I'm a " + genericField.getClass());
System.out.println("Am I a string? " + (genericField instanceof String));
System.out.println("Am I a long? " + (genericField instanceof Long));
}
public static void main(String [] args) {
TestClass<String> genericString = new TestClass<>("Hello");
TestClass<Long> genericLong = new TestClass<>(111111L);
genericString.printTypeInfo();
System.out.println("------------------");
genericLong.printTypeInfo();
}
}
It gives me the following result: 它给了我以下结果:
Hi I'm a class java.lang.String
Am I a string? true
Am I a long? false
------------------
Hi I'm a class java.lang.Long
Am I a string? false
Am I a long? true
Seems like type information is readily available at runtime. 似乎类型信息在运行时很容易获得。 What am I missing here?
我在这里错过了什么?
TestClass<Number> genericNumber = new TestClass<>(42L);
genericNumber.printTypeInfo();
This will print Hi I'm a Long
instead of Hi I'm a Number
. 这将打印
Hi I'm a Long
而不是Hi I'm a Number
。 You can see that genericField
is a Long
but you can't see that T
was instantiated as Number
. 您可以看到
genericField
是Long
但您无法看到T
被实例化为Number
。
Here's an example of something you can't do because of type erasure. 这是一个由于类型擦除而无法做的事情的例子。
TestClass<?> generic = new TestClass<String>("Hello");
if (generic instanceof TestClass<String>) {
System.out.println("It holds a string!");
}
else if (generic instanceof TestClass<Long>) {
System.out.println("It holds a long!");
}
You can determine the type of any given object in genericField
at runtime, but you cannot determine the difference between a TestClass<X>
and a TestClass<Y>
at runtime without examining some members that you know happen to be constrained by the generic type. 您可以在运行时确定
genericField
中任何给定对象的类型,但是您无法在运行时确定TestClass<X>
和TestClass<Y>
之间的差异,而无需检查您知道碰巧受泛型类型约束的某些成员。 That is you cannot determine the type parameter of a TestClass<...>
given an instance of a TestClass
alone. 也就是说,如果仅给出一个
TestClass
实例,则无法确定TestClass<...>
的类型参数。
Your code displays the type of genericField
's value, not the parameterized type of the TestClass
instance. 您的代码显示
genericField
的值的类型, 而不是 TestClass
实例的参数化类型。 Try printing this.getClass()
and you'll see its identical in both cases. 尝试打印
this.getClass()
,你会发现它在两种情况下都是相同的。
What you are "missing" is this: You are (understandably) making an incorrect connection between the fact that genericField
itself holds an object (with a type) and the fact that TestClass
has a generic type parameter. 你“缺少”的是这样的:你(可以理解)在
genericField
本身拥有一个对象(带有一个类型)和TestClass
有一个泛型类型参数这一事实之间建立了一个不正确的联系。 You are confusing the ability to determine the type of genericField
s value with the ability to determine the type parameter specified to a TestClass
. 您可以通过确定为
TestClass
指定的类型参数的能力来确定genericField
值的类型。 That is, while you can deduce what the type parameter was based on your knowledge that genericField
is T
, this is not the same as directly being able to determine what T
was, which is impossible. 也就是说,虽然您可以根据您对
genericField
为T
知识推断出类型参数是什么,但这与直接确定T
是什么不同,这是不可能的。
Another way to look at the previous paragraph is to consider these points: 查看上一段的另一种方法是考虑以下几点:
If TestClass
had no members of type T
then there's no other way you could extract T
. 如果
TestClass
没有类型为T
成员,则没有其他方法可以提取T
Your code only "determines" what T
was based on your own personal knowledge that genericField
was declared as holding that same type (and therefore the object in it must be of that type, and therefore you can conclude that the generic parameter was likely that same type or some supertype of it). 您的代码仅“确定”
T
基于您自己的个人知识,即genericField
被声明为持有相同类型(因此其中的对象必须属于该类型,因此您可以得出结论通用参数可能是相同的)类型或某种超类型)。
If you did not use generics, and genericField
was just an Object
, you'd still be able to determine the type of the object in genericField
. 如果你没有使用泛型,而
genericField
只是一个Object
,你仍然可以在genericField
确定对象的类型。 That is, its type is "independent" of the generic type, except when you use generics the compiler places a constraint on the type. 也就是说,它的类型与泛型类型“独立”,除非使用泛型,否则编译器会对类型进行约束。 It's still just an arbitrary object after compilation, regardless of whether or not you used generics (which are really just a convenience, as you could do all of this without generics and just use
Object
and lots of casts instead). 编译后它仍然只是一个任意对象,无论你是否使用泛型(这实际上只是一种方便,因为你可以在没有泛型的情况下完成所有这些,只需使用
Object
和大量的强制转换)。
Consider also the possibility of a TestClass<Base>
, where genericField
was assigned a Derived
. 还要考虑
TestClass<Base>
的可能性,其中genericField
被赋予了Derived
。 Your code would correctly show that genericField
was a Derived
, but you have no way of knowing that the type parameter was a Base
vs. a Derived
, because the information was erased. 您的代码将正确显示
genericField
是Derived
,但您无法知道类型参数是Base
与Derived
,因为信息已被删除。
Also, to ram the above points home even further: 此外,将以上几点进一步归结为家:
TestClass<String> genericString = new TestClass<String>("Hello");
TestClass<?> kludge = genericString;
TestClass<Long> genericLongButNotReally = (TestClass<Long>)kludge;
genericLongButNotReally.printTypeInfo();
Outputs the info for a String
(this is why those "unchecked conversion" warnings are given, to prevent strange things like this), not caring about the fact that genericLongButNotReally
was specified with a Long
type parameter. 输出
String
的信息(这就是给出那些“未经检查的转换”警告的原因,以防止出现这种奇怪的事情),而不关心使用Long
类型参数指定genericLongButNotReally
的事实。 The kludge is necessary to circumvent the good protection that the compiler offers when you use generic types; 当您使用泛型类型时,必须克服编译器提供的良好保护; but at runtime it does not care.
但在运行时它并不关心。
Being able to get the type of a variable and the type of an object are two different things. 能够获得变量的类型和对象的类型是两回事。
Just because you can get the type of genericField
doesn't mean that you can see that T was a number. 仅仅因为你可以得到
genericField
的类型并不意味着你可以看到T是一个数字。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.