Guava TypeToken и общие классы
Я использую класс 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
?
2 ответа
Чтобы сделать это, вам нужен тот же трюк анонимного подкласса, когда вы создаете экземпляр MyGenericClass
:
new MyGenericClass<String>() {}
Если вы сделаете это, то вы получите String
вернулся getRecordType
, Причина, почему это необходимо, объясняется в JavaDocs для этого TypeToken
конструктор
Клиенты создают пустой анонимный подкласс. Это позволяет встроить параметр типа в иерархию типов анонимного класса, чтобы мы могли восстановить его во время выполнения, несмотря на удаление.
Чтобы добавить некоторые скучные детали к ответу Яна: было бы хорошо, если TypeToken
работал так, как вы ожидали, но это невозможно. Когда вы объявляете
public class MyGenericClass<T> implements MyInterface {...}
JVM видит что-то вроде
public class MyGenericClass<Object> implements MyInterface {...}
из-за стирания.
Но когда вы объявляете
public class MyStringImpl extends MyGenericClass<String> {...}
тогда в определении MyStringImpl
используемые дженерики записываются и могут быть получены через Class#getGenericSuperclass()
, Это (часть) магия позади TypeToken
,