我在项目中使用了Guava TypeToken类,但是我得到了意想不到的结果。
我有MyGenericClass<T>
:
public class MyGenericClass<T> implements MyInterface {
private TypeToken<T> recordType;
public MyGenericClass(String name) {
this.recordType = new TypeToken<T>(getClass()) {};
// ...
}
// ...
@SuppressWarnings("unchecked")
protected Class<T> getRecordType() {
return (Class<T>) recordType.getRawType();
}
}
因此,如果我通过new MyGenericClass<String>()
实例化对象,然后调用getRecordType()
,我希望获得java.lang.String
,而不是java.lang.Object
。
但是,如果我扩展泛型类:
public class MyStringImpl extends MyGenericClass<String> {
// ...
}
并实例化这个新类:new MyStringImpl()
然后我得到了正确的结果。
为什么会这样?这是TypeToken
的预期行为吗?
答案 0 :(得分:14)
在Ian的回答中添加一些无聊的细节:如果TypeToken
按预期方式工作会很好,但这是不可能的。当你宣布
public class MyGenericClass<T> implements MyInterface {...}
JVM看起来像
public class MyGenericClass<Object> implements MyInterface {...}
由于擦除。
但是当你宣布
时public class MyStringImpl extends MyGenericClass<String> {...}
然后在MyStringImpl
的定义中,记录了使用的泛型,并且可以通过Class#getGenericSuperclass()
获得。这是TypeToken
背后的魔力(<一部分)。
答案 1 :(得分:13)
要实现此功能,在实例化MyGenericClass
时需要使用相同的匿名子类技巧:
new MyGenericClass<String>() {}
如果您这样做,那么String
会返回getRecordType
。必要的原因在JavaDocs for that TypeToken
constructor。
客户端创建一个空的匿名子类。这样做会将type参数嵌入到匿名类的类型层次结构中,这样我们就可以在运行时重新构建它,尽管擦除了。