Странности Generic типов Java |
Я множество раз слышал о том, что дизайн Generic типов в Java является неудачным. По большей части претензии сводятся к отсутствию поддержки примитивных типов (которую планируют добавить) и к стиранию типов, а конкретнее — невозможности получить фактический тип параметра в рантайме. Лично я не считаю стирание типов проблемой, как и дизайн Generic-ов плохим. Но есть моменты, которые меня порядком раздражают, но при этом не так часто упоминаются.
Например, мы знаем, что метод Class#getAnnotation
параметризован и имеет следующую сигнатуру: public A getAnnotation(Class annotationClass)
. Значит, можно писать вот такой код:
Deprecated d = Object.class.getAnnotation(Deprecated.class);
Тут я решаю вынести Object.class
в отдельную переменную и код перестаёт компилироваться:
Class clazz = Object.class;
// incompatible types:
// java.lang.annotation.Annotation cannot be converted to java.lang.Deprecated
Deprecated d = clazz.getAnnotation(Deprecated.class);
Где я ошибся?
Ошибся я в том, что не параметризовал тип переменной clazz
.
Получается, что стирание у типа Class
так же стирает типы во всех его методах! Зачем так было делать — понятия не имею. Вносим минимальное исправление в код и всё работает как надо.
Class clazz = Object.class;
Deprecated d = clazz.getAnnotation(Deprecated.class);
Второй пример немного надуманный, но показательный. Представьте, есть у вас такой простой класс:
class Ref {
private T value = null;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
Имея переменную ref
я решу написать такой код. Что с ним может произойти плохого?
ref.setValue(ref.getValue());
Разумно было бы считать, что он всегда скомпилируется, но это не так! Вам всего лишь стоит объявить переменную ref
с типом Ref h = new HasArrayList<>(new ArrayList<>());
ArrayList list = h.getList();
Параметр T
класса HasArrayList
имеет верхнюю границу равную ArrayList
, а значит при стирании типов код всё ещё должен компилироваться.
HasArrayList h = new HasArrayList<>(new ArrayList<>());
// incompatible types: java.util.List cannot be converted to java.util.ArrayList
ArrayList list = h.getList();
Ну вот, опять не работает. Сейчас то что не так?
Не так то, что в сигнатуре метода getList
возвращаемым типом является List
, а компилятору просто лень расставлять явные приведения типов. Исправляется всё очень просто — надо переопределить данный метод в подклассе.
class HasArrayList extends HasList {
public HasArrayList(T list) {
super(list);
}
@Override
public T getList() {
return super.getList();
}
}
При этом компилятор сгенерирует синтетический bridge метод, возвращающий ArrayList
, и именно он и будет вызван. Очевидно же...
Вообще, если у класса есть тип-параметр, то лучше в коде его не игнорировать, в крайнем случае можно указать
Комментировать | « Пред. запись — К дневнику — След. запись » | Страницы: [1] [Новые] |