Перечисления Java великолепны. Как и дженерики. Конечно, мы все знаем ограничения последнего из-за стирания типа. Но есть одна вещь, которую я не понимаю: почему я не могу создать перечисление вот так:
public enum MyEnum<T> {
LITERAL1<String>,
LITERAL2<Integer>,
LITERAL3<Object>;
}
Этот параметр общего типа, <T>
в свою очередь, может быть полезен в различных местах. Представьте параметр общего типа для метода:
public <T> T getValue(MyEnum<T> param);
Или даже в самом классе enum:
public T convert(Object o);
Более конкретный пример № 1
Поскольку приведенный выше пример может показаться слишком абстрактным для некоторых, вот более реальный пример того, почему я хочу это сделать. В этом примере я хочу использовать
- Перечисляет, потому что тогда я могу перечислить конечный набор ключей свойств
- Обобщения, потому что тогда у меня может быть безопасность типов на уровне метода для хранения свойств
public interface MyProperties {
public <T> void put(MyEnum<T> key, T value);
public <T> T get(MyEnum<T> key);
}
Более конкретный пример № 2
У меня есть перечисление типов данных:
public interface DataType<T> {}
public enum SQLDataType<T> implements DataType<T> {
TINYINT<Byte>,
SMALLINT<Short>,
INT<Integer>,
BIGINT<Long>,
CLOB<String>,
VARCHAR<String>,
...
}
Каждый литерал перечисления, очевидно, будет иметь дополнительные свойства, основанные на универсальном типе <T>
, и в то же время быть перечислением (неизменяемое, одноэлементное, перечислимое и т. Д. И т. Д.)
Вопрос:
Никто не думал об этом? Это ограничение, связанное с компилятором? Учитывая тот факт, что ключевое слово « enum » реализовано как синтаксический сахар, представляющий сгенерированный код для JVM, я не понимаю этого ограничения.
Кто может объяснить это мне? Прежде чем ответить, подумайте об этом:
- Я знаю, что общие типы стираются :-)
- Я знаю, что есть обходные пути с использованием объектов класса. Они обходные пути.
- Универсальные типы приводят к генерации типов, сгенерированной компилятором, где это применимо (например, при вызове метода convert ()
- Универсальный тип <T> будет в перечислении. Следовательно, он связан каждым из литералов перечисления. Следовательно, компилятор будет знать, какой тип применять при написании чего-то вроде
String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
- То же самое относится к параметру универсального типа в
T getvalue()
методе. Компилятор может применять приведение типов при вызовеString string = someClass.getValue(LITERAL1)
enum
в идиому «типизированное перечисление», который мы использовали до Java 1.5. Внезапно вы можете настроить параметры перечисления. Это, вероятно, то, что я собираюсь сделать сейчас.Ответы:
Это сейчас обсуждается с расширенными перечислениями JEP-301 . Пример, приведенный в JEP, это именно то, что я искал:
К сожалению, JEP все еще борется со значительными проблемами: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html
источник
Ответ в вопросе:
Ни один из этих двух методов невозможен, так как тип аргумента стирается.
Однако, чтобы реализовать эти методы, вы можете построить свой enum следующим образом:
источник
public T convert(Object);
. Я предполагаю, что этот метод может, например, сузить группу различных типов до <T>, который, например, является String. Конструкция объекта String выполняется во время выполнения - ничего не делает компилятор. Следовательно, вам нужно знать тип среды выполнения, такой как String.class. Или я что-то упустил?public final static String FOO;
статический блокstatic { FOO = "bar"; }
- константы также оцениваются, даже если они известны во время компиляции.Потому что ты не можешь. Шутки в сторону. Это можно добавить к спецификации языка. Это не было Это добавило бы некоторую сложность. Эта выгода от стоимости означает, что она не является приоритетной.
Обновление: в настоящее время добавляется к языку в JEP 301: Enhanced Enums .
источник
В ENUM есть другие методы, которые не работают. Что
MyEnum.values()
вернется?Как насчет
MyEnum.valueOf(String name)
?Для valueOf, если вы думаете, что компилятор может сделать общий метод, такой как
чтобы это так назвать
MyEnum<String> myStringEnum = MyEnum.value("some string property")
, это тоже не сработало бы. Например, что если вы позвонитеMyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")
? Невозможно реализовать этот метод для правильной работы, например, чтобы выдать исключение или вернуть ноль, когда вы вызываете его как,MyEnum.<Int>value("some double property")
из-за стирания типа.источник
MyEnum<?>[] values()
иMyEnum<?> valueOf(...)
MyEnum<Int> blabla = valueOf("some double property");
потому что типы не совместимы. Также в этом случае вы хотите получить значение null, потому что вы хотите вернуть MyEnum <Int>, который не существует для двойного имени свойства, и вы не можете заставить этот метод работать должным образом из-за удаления.Enum
него много других полезных функций, и эта будет необязательной и очень полезной ...Откровенно говоря, это больше похоже на решение проблемы, чем что-либо еще.
Вся цель перечисления java состоит в том, чтобы смоделировать перечисление экземпляров типов, которые имеют общие свойства таким образом, что обеспечивает согласованность и богатство, превосходящие сопоставимые представления String или Integer.
Возьмите пример перечисления учебника. Это не очень полезно или не соответствует:
Зачем мне хотеть, чтобы на разных планетах были разные преобразования типов? Какую проблему это решает? Оправдывает ли это усложнение языковой семантики? Если мне нужно такое поведение, является ли enum лучшим инструментом для достижения этого?
Кроме того, как бы вы управляли сложными конверсиями?
например
Достаточно просто
String
Integer
указать имя или порядковый номер. Но дженерики позволят вам поставить любой тип. Как бы вы справились с преобразованием вMyComplexClass
? Теперь вы смешиваете две конструкции, заставляя компилятор знать, что существует ограниченное подмножество типов, которые могут быть предоставлены универсальным перечислениям, и вводите дополнительную путаницу в концепцию (Generics), которая, похоже, уже ускользает от многих программистов.источник
interface Converter<IN, OUT> { OUT convert(IN in); } <E> Set<E> convertListToSet(List<E> in, Converter<? super List<E>, ? extends Set<E>> converter) { return converter.convert(in); }
Мы должны каждый раз вручную определять, какой тип потреблен, а какой произведен, и соответственно определять границы.Becasue "enum" является аббревиатурой для перечисления. Это просто набор именованных констант, которые стоят вместо порядковых номеров, чтобы сделать код более читабельным.
Я не понимаю, каким может быть предполагаемое значение параметризованной константы.
источник
java.lang.String
:public static final Comparator<String> CASE_INSENSITIVE_ORDER
. Теперь ты видишь? :-)CASE_INSENSITIVE_ORDER
... ЕслиComparator<T>
бы перечисление было, почему бы не иметь литералы с каким-либо связанным ограничением<T>
?Я думаю, потому что в основном нельзя перечислить Enums
Где бы вы установили класс T, если бы JVM позволила вам это сделать?
Перечисление - это данные, которые должны быть всегда одинаковыми или, по крайней мере, динамически не изменяться.
новый MyEnum <> ()?
Тем не менее, следующий подход может быть полезным
источник
setO()
метод наenum
. Я считаю перечисления константами и для меня это подразумевает неизменность . Поэтому, даже если это возможно, я бы не стал этого делать.