Почему параметр типа Java не может иметь нижнюю границу?
Я понимаю, что вы не можете привязать параметр типа generics Java к нижней границе (т. Е. Используя ключевое слово super
). Я читал то, что Чейтолог Angelika Langer Generics должен был сказать по этому вопросу . Они говорят, что это в основном сводится к тому, что нижняя граница бесполезна («не имеет никакого смысла»).
Я не уверен. Я могу представить себе использование для них, чтобы помочь вам быть более гибкими для вызывающих методов библиотеки, которые создают типизированный результат. Представьте метод, который создал список массивов заданного пользователем размера и заполнил его пустой строкой. Простое заявление было бы
public static ArrayList createArrayListFullOfEmptyStrings(int i);
Но это излишне ограничительно для ваших клиентов. Почему они не могут вызвать ваш метод следующим образом:
- Длинный список операторов if в Java
- Шаблон строителя и большое количество обязательных параметров
- Как установить фоновое изображение в JPanel
- Ошибка «Не удалось получить приложение» при запуске Eclipse
- Лучший подход к обработке исключений функциональным способом
//should compile List l1 = createArrayListFullOfEmptyStrings(5); List l2 = createArrayListFullOfEmptyStrings(5); List l3 = createArrayListFullOfEmptyStrings(5); //shouldn't compile List l4 = createArrayListFullOfEmptyStrings(5);
В этот момент у меня возникнет соблазн попробовать следующее определение:
public static List createArrayListFullOfEmptyStrings(int size) { List list = new ArrayList(size); for(int i = 0; i < size; i++) { list.add(""); } return list; }
Но он не будет компилироваться; ключевое слово super
является незаконным в этом контексте.
Мой пример выше плохого примера (игнорируя то, что я говорю ниже)? Почему здесь нет нижней границы? И если это было бы полезно, какова реальная причина, по которой это запрещено в Java?
PS
Я знаю, что лучшая организация может быть примерно такой:
public static void populateListWithEmptyStrings(List list, int size); List list = new ArrayList(); populateListWithEmptyStrings(list, 5);
Можем ли мы с этой целью притворяться, что по требованию нам нужно выполнить обе операции в одном вызове метода?
редактировать
@Tom G (справедливо) спрашивает, какая польза от List
будет иметь над List
. Во-первых, никто не сказал, что возвращенный список неизменен, так что вот одно преимущество:
List l2 = createArrayListFullOfEmptyStrings(5); l2.add(new StringBuilder("foo").append("bar"));
- Отображение сервлета с использованием web.xml
- Подписать CSR с помощью Bouncy Castle
- Соображения производительности для keySet () и entrySet () карты
- Разница между applicationContext.xml и spring-servlet.xml в Spring Framework
- Преобразование шестнадцатеричной строки (hex) в двоичную строку
- Поймать все Исключения, а также вернуть пользовательские ошибки в Джерси
- Конечные аргументы в методах интерфейса - в чем смысл?
- Наследование в Java - создание объекта подclassа вызывает также конструктор суперclassа. Почему именно?
В принципе, его недостаточно полезно.
Я думаю, что ваш пример указывает на единственное преимущество нижней границы, функцию, которую часто Restricted Instantiation
:
Суть заключается в том, что все, что «супер» связало бы с вами, – это ограничение, что только супертипы числа могут использоваться как аргументы типа . ….
Но, как указывают другие сообщения, полезность даже этой функции может быть ограничена.
Из-за характера polymorphismа и специализации верхние границы гораздо полезнее нижних границ, как описано в FAQ ( Доступ к нестационарным членам и стирание типов ). Я подозреваю, что сложность, введенная нижними границами, не стоит его ограниченного значения.
OP: Я хочу добавить, я думаю, вы показали, что это полезно, просто недостаточно полезно. Придумайте неопровержимые примеры использования убийц, и я верну JSR. 🙂
спецификация действительно говорит о нижних границах параметров типа, например
4.10.2
переменная типа является прямым супертипом ее нижней границы.
5.1.10
новая переменная типа … нижняя граница которой
Похоже, что переменная типа имеет только (ненулевую) нижнюю границу, если она является синтетической в результате захвата подстановочных знаков. Что делать, если язык допускает нижние границы для всех параметров типа? Вероятно, это не вызывает больших проблем, и исключается только упрощение обобщения (ну …). Обновление говорит о том, что теоретическое исследование параметров нижнего ограниченного типа не проводится полностью.
Обновление: документ, утверждающий нижние границы, в порядке: «Недопустимость типа Java-типа: можем ли мы его исправить» Daniel Smith
RETRACT: следующий аргумент неверен. Пример OP является законным.
Ваш конкретный пример не очень убедителен. Во-первых, это не безопасный тип. Возвращенный список действительно представляет собой List
, его небезопасно рассматривать как другой тип. Предположим, что ваш код компилируется:
List l2 = createArrayListFullOfEmptyStrings(5);
то мы можем добавить к нему не-String, что неверно
CharSequence chars = new StringBuilder(); l2.add(chars);
Ну, List
не является, но несколько похож на список CharSequence. Ваша потребность может быть решена с помощью шаблона:
public static List createArrayListFullOfEmptyStrings(int size) // a list of some specific subtype of CharSequence List l2 = createArrayListFullOfEmptyStrings(5); // legal. can retrieve elements as CharSequence CharSequence chars = l2.get(0); // illegal, won't compile. cannot insert elements as CharSequence l2.add(new StringBuilder());
Это больше, чем ответ, это другой вариант (возможно, убийца?). У меня есть помощник ModelDecorator. Я хочу, чтобы у него был следующий открытый API
class ModelDecorator{ public static ModelDecorator create(Class clazz); public T from(SUPER fromInstance); }
Таким образом, заданные classы A, B расширяют A, его можно использовать следующим образом:
A a = new A(); B b = ModelDecorator.create(B.class).from(a);
Но я хочу иметь ограничения на T и SUPER, поэтому я убеждаюсь, что только субclassы могут быть созданы с использованием API. В этот момент я могу сделать:
C c = new C(); B b = ModelDecorator.create(B.class).from(c);
Где B НЕ НАЙТИ НА C.
Очевидно, если бы я мог:
public T from(SUPER fromInstance);
Это решило бы мою проблему.
Какое преимущество при вводе списка дает вам в этот момент? Когда вы перебираете возвращаемую коллекцию, вы все равно можете сделать следующее:
for(String s : returnedList) { CharSequence cs = s; //do something with your CharSequence }
Хм, хорошо, давайте работать с этим. Вы определяете метод:
public static
Что это значит? Это означает, что если я вызову ваш метод, я верну список суперclassа String. Возможно, он возвращает список String. Возможно, он возвращает список объектов. Я не знаю.
Круто.
List
По вашему мнению, это должно скомпилировать. Но это не так! Я могу поместить Integer в список Object – l1.add(3)
. Но если вы возвращаете список String, то это должно быть незаконным.
List
По вашему мнению, это должно скомпилировать. Но это не так! l3.get(1)
должен всегда возвращать String … но этот метод мог бы вернуть список объектов, что означает, что l3.get (1) может быть, возможно, целым.
Единственное, что работает, это
List l5 = createArrayListFullOfEmptyStrings(5);
Все, что я знаю, это то, что я могу смело называть l4.put("foo")
, и я могу безопасно получить Object o = l4.get(2)
.