Выкладываю данный пост с целью получения обратной связи от хабра-сообщества Java-программистов. При наличии достаточного количества голосов «за» эту фичу можно будет отправить для рассмотрения в Sun.
После длительной работы с Java 5 и Java 6 и небольшой аналитики я пришел к тому, что generic'и в Java имеют ряд ограничений, которые не дают реализовать решение красивым способом, и обходить их порой приходится очень нетривиально. Данное высказывание относится к разработке подсистем, которые активно используют generic'и (доходит до 3-4 generic-типов со сложной иерархией в описании класса).
Приведу примеры кода, которые явно указывают на данные ограничения, а также неудобство использования (рассматривать только синтаксис):
Для решения озвученных проблемы и неудобства использования, снижающего читаемость кода, предлагается расширить синтаксис языка и ввести алиасы (aliases).
Алиас представляет собой generic-тип, объявляемый на основе других generic-типов, который может использоваться в пределах видимости.
Проще всего показать на примере. Преобразуем приведенный выше код с использованием алиасов:
Предполагается, что алиасы никак не будут отражены в байт-коде, т.е. они фактически являются расширением синтаксиса языка, но не семантики. Также они позволяют зафиксировать generic-тип, что в некоторых случаях (см. пример выше) позволяет избежать cast-ов и warning'ов.
Проблема
После длительной работы с Java 5 и Java 6 и небольшой аналитики я пришел к тому, что generic'и в Java имеют ряд ограничений, которые не дают реализовать решение красивым способом, и обходить их порой приходится очень нетривиально. Данное высказывание относится к разработке подсистем, которые активно используют generic'и (доходит до 3-4 generic-типов со сложной иерархией в описании класса).
Приведу примеры кода, которые явно указывают на данные ограничения, а также неудобство использования (рассматривать только синтаксис):
public class A { public <X> void method(List<? extends X> list) { // processElement(list, list.get(0)); - compilation error processElement((List) list, (Object) list.get(0)); // compilation warning } public <T> void processElement(List<T> list, T object) { } public static class B<T extends I1 & I2> { } // Приходится повторять объявления generic-типов public static class С<T extends I1 & I2> extends A.B<T> { } }
Решение
Для решения озвученных проблемы и неудобства использования, снижающего читаемость кода, предлагается расширить синтаксис языка и ввести алиасы (aliases).
Алиас представляет собой generic-тип, объявляемый на основе других generic-типов, который может использоваться в пределах видимости.
Проще всего показать на примере. Преобразуем приведенный выше код с использованием алиасов:
public class A { public <X> void method(List<? extends X> list) { // processElement(list, list.get(0)); - Ошибка компиляции /* Определение ? extends X как T для типа переменной list (т.е. List<? extends X> -> List<T>). Видимость подобного определения ограничивается блоком. При этом данное определение используется как же, как generic-тип T. */ List<T> list1 = alias<T>(list); processElement(list1, list1.get(0)); // Корректно } public <T> void processElement(List<T> list, T object) { } /* Алиас N может использоваться везде, где могла бы быть использована связка I1 & I3 (в generic'ах). Алиас N может использоваться везде, где мог бы быть использован любой из интерфейсов I1 и I3. Алиас N может дополнительно использоваться как тип переменной, возвращаемое значение метода или параметр метода. У алиаса есть уровень доступа. */ public alias N = I1 & I2; public static class B<T extends N> { } // Приходится повторять объявление одного generic-типа вместо сложной связки public static class С<T extends N> extends A.B<T> { } }
Предполагается, что алиасы никак не будут отражены в байт-коде, т.е. они фактически являются расширением синтаксиса языка, но не семантики. Также они позволяют зафиксировать generic-тип, что в некоторых случаях (см. пример выше) позволяет избежать cast-ов и warning'ов.