我有这个代码,编译:
new TypeToken<ArrayList<ServerTask>>() {}.getType()
然后我尝试了
ArrayList<ServerTask>.class
无法编译。
我是Java编程的新手(来自C#),我认为T.class
与C#中的typeof(T)
完全相同。显然有一些我不理解的基本内容,所以我的问题是ArrayList<ServerTask>.class
出了什么问题,我别无选择,只能使用new TypeToken<ArrayList<ServerTask>>() {}.getType()
?是否有更短(更好,更健全)的形式?
感谢。
答案 0 :(得分:11)
不幸的是(?)Java使用Type Erasure实现了泛型。
这意味着没有构造来获取泛型类型,并且在运行时,您的ArrayList实际上只存储对象。
确切地说,Java中的Generics是一个仅编译时的构造。它们仅在编译时保证类型安全。
编辑: 我刚注意到这段代码 -
new TypeToken<ArrayList<ServerTask>>() {}.getType()
这是类型擦除限制的解决方法。如果扩展具有特定类型的泛型类,则会保留类型信息。例如:
public interface List<T> {
}
public class StringList implements List<String> {
// The bytecode of this class contains type information.
}
在您的示例中,您可以轻松扩展类TypeToken
,从而使编译器生成包含类型信息的匿名内部类。 TypeToken.getType()
的实现将使用反射为您提供此类型信息。
EDIT2:有关更详细的说明,请查看Reflecting Generics。
答案 1 :(得分:1)
您的第一个代码示例创建了一个带有具体通用绑定的TypeToken的匿名子类。返回的类型将是Class
的实例。但要注意,因为
(new TypeToken<ArrayList<ServerTask>>() {}.getType()).getClass(TypeToken.class)
将返回false!
在您的第二个代码示例中,您尝试执行Java不支持的操作,因为类型擦除实现了泛型。没有代表ArrayList
的类,其泛型参数被绑定...从编译器的角度来看,无论泛型类型如何,ArrayList
都是ArrayList
,因此表达式不会编译。
这两段代码都可能无法正常运行。如果您需要使用通用参数来玩类,您可能需要查看gentyref,我发现这对于简化API非常有帮助,可以询问您尝试获得答案的各种问题到。
答案 2 :(得分:1)
关于“.class”构造,它不是运算符也不是实例成员,它实际上是一种告诉语言“在点之前找到此类名的Class对象”的方法。是一种构建类文字的方法。
如JLE section 15.8.2中所述,如果您尝试将其与参数化类型(以及其他类型)一起使用,编译器将会抱怨。
答案 3 :(得分:0)
Bringer128给出的示例被视为一种称为pseudo-typedef antipattern的反模式。以下是替代方案:
class TokenUtil{
public static <T> TypeToken<T> newTypeToken(){
return new TypeToken<T>();
}
public static void main(String[] args) {
TypeToken<ArrayList<ServerTask>> typeTok = TokenUtil.newTypeToken();
Type type = typeTok.getType();
}
}