Мое, очевидно, неправильное понимание Дженериков Java было до сих пор, то Стирание Типа удаляет всю информацию о типе, таким образом, что нет ничего вообще во времени выполнения. Недавно я наткнулся на фрагмент кода, где я должен был спросить меня: Как взлом это работает? Упрощенный, это представляет как:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public abstract class SuperClass<T> {
private final Type type;
protected SuperClass(){
ParameterizedType parameterizedType =
(ParameterizedType) getClass().getGenericSuperclass();
type = parameterizedType.getActualTypeArguments()[0];
}
public void tellMyType(){
System.out.println("Hi, my type parameter is " + type);
}
}
и
public class Example {
public static void main(String[] args) {
SuperClass sc = new SuperClass<Integer>(){};
sc.tellMyType();
}
}
Выполнение Основного Класса приводит к Hi, my type parameter is class java.lang.Integer
.
Что мы видим, вот, что информация о типе T также доступна во времени выполнения, которое противоречит моему начальному пониманию.
Таким образом, мой вопрос: Почему компилятор сохраняет это? Это требуется для некоторого внутреннего поведения JVM или является там каким-либо разумным объяснением этого эффекта?
Из http://www.artima.com/weblogs/viewpost.jsp?thread = 208860 :
Оказывается, хотя JVM не отслеживает фактические аргументы типа для экземпляров универсального класса, она отслеживает фактические аргументы типа для подклассов универсальных классов. Другими словами, , в то время как новый
ArrayList
на самом деле просто новый() ArrayList ()
во время выполнения , если класс расширяетArrayList
, то JVM знает, чтоString
является фактическим аргументом типа дляСписок параметров типа
.
В вашем случае вы создаете анонимный подкласс параметризованного типа, поэтому информация о типе сохраняется. См. Статью для подробного объяснения.
Параметры типа удаляются только из динамических типов (т. Е. Типа создаваемого объекта):
Object o = new ArrayList<String>(); // String erased
Он сохраняется в статических типах (т. Е. Типах поля, аргумента и возвращаемого значения, throws
, объявления суперкласса и суперинтерфейса):
class Test implements Superclass<String> { // String retained
// Accessible via Class.getGenericSuperclass()
private List<Integer> l; // Integer retained (via Field.getGenericType())
public void test(List<Long> l) {} // Long retained (via Method.getGenericParameterTypes())
// Character retained (via Method.getGenericReturnType())
public List<Character> test() { return null; }
}
В вашем случае вы создаете анонимный подкласс SuperClass
, поэтому параметр типа сохраняется в объявлении суперкласса.
Google Guice использует это для создания TypeLiteral
для представления общих классов во время выполнения. Например,
TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};
можно использовать, но
Class<List<String>> list = List<String>.class;
не компилируется.
Этот метод известен как «токен супертипа» (см. статью Нила Гафтера по этому поводу).