-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51


Странности Generic типов Java

Среда, 31 Мая 2017 г. 21:34 + в цитатник

Я множество раз слышал о том, что дизайн Generic типов в Java является неудачным. По большей части претензии сводятся к отсутствию поддержки примитивных типов (которую планируют добавить) и к стиранию типов, а конкретнее — невозможности получить фактический тип параметра в рантайме. Лично я не считаю стирание типов проблемой, как и дизайн Generic-ов плохим. Но есть моменты, которые меня порядком раздражают, но при этом не так часто упоминаются.


1


Например, мы знаем, что метод 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);

2


Второй пример немного надуманный, но показательный. Представьте, есть у вас такой простой класс:


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, и именно он и будет вызван. Очевидно же...


Вообще, если у класса есть тип-параметр, то лучше в коде его не игнорировать, в крайнем случае можно указать

https://habrahabr.ru/post/329550/

Метки:  

 

Добавить комментарий:
Текст комментария: смайлики

Проверка орфографии: (найти ошибки)

Прикрепить картинку:

 Переводить URL в ссылку
 Подписаться на комментарии
 Подписать картинку