Java有类型擦除,人们说人们无法在运行时确定通用对象的类型。请考虑以下代码
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();
}
}
它给了我以下结果:
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
似乎类型信息在运行时很容易获得。我在这里缺少什么?
答案 0 :(得分:4)
TestClass<Number> genericNumber = new TestClass<>(42L);
genericNumber.printTypeInfo();
这将打印Hi I'm a Long
而不是Hi I'm a Number
。您可以看到genericField
是Long
,但您无法看到T
被实例化为Number
。
这是一个由于类型擦除而无法做到的事情的例子。
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!");
}
答案 1 :(得分:4)
您可以在运行时确定genericField
中任何给定对象的类型,但是如果不检查您知道的某些成员,则无法在运行时确定TestClass<X>
和TestClass<Y>
之间的差异碰巧受到泛型类型的约束。也就是说,如果仅给出TestClass<...>
的实例,则无法确定TestClass
的类型参数。
您的代码显示genericField
值的类型,不 TestClass
实例的参数化类型。尝试打印this.getClass()
,您会发现两种情况都相同。
你“缺少”的是:你(可以理解)在genericField
本身拥有一个对象(带有一个类型)和TestClass
有一个对象的事实之间建立了一个错误的联系泛型类型参数。您可以确定genericField
s值的类型,并且能够确定为TestClass
指定的类型参数。也就是说,虽然您可以根据genericField
T
的 知识推断出类型参数是什么,但这与直接确定T
的内容不同1}},这是不可能的。
查看上一段的另一种方法是考虑以下几点:
如果TestClass
没有T
类型的成员,那么您无法提取T
。您的代码仅根据您自己的个人知识“确定”T
是什么genericField
被声明为持有相同类型(因此其中的对象必须属于该类型,因此您可以得出结论泛型参数可能是相同的类型或某种超类型。)
如果您没有使用泛型,genericField
只是Object
,您仍然可以在genericField
中确定对象的类型。也就是说,它的类型与泛型类型“独立”,除非使用泛型,否则编译器会对类型进行约束。编译后它仍然只是一个任意对象,无论你是否使用泛型(这实际上只是一种便利,因为你可以在没有泛型的情况下完成所有这些,只需使用Object
和大量的强制转换)。 / p>
还要考虑TestClass<Base>
的可能性,其中genericField
被分配了Derived
。您的代码会正确显示genericField
是Derived
,但您无法知道类型参数是Base
与Derived
,因为信息是擦除。
此外,将上述观点推向家乡:
TestClass<String> genericString = new TestClass<String>("Hello");
TestClass<?> kludge = genericString;
TestClass<Long> genericLongButNotReally = (TestClass<Long>)kludge;
genericLongButNotReally.printTypeInfo();
输出String
的信息(这就是为什么会给出那些“未经检查的转换”警告,以防止出现这种奇怪的事情),而不是关心genericLongButNotReally
是用{指定Long
的事实{1}}类型参数。当您使用泛型类型时,必须克服编译器提供的良好保护;但在运行时它并不关心。
答案 2 :(得分:1)
能够获得变量的类型和对象的类型是两回事。
仅仅因为你可以得到genericField
的类型并不意味着你可以看到T是一个数字。