Почему литералы Java enum не должны иметь общих параметров типа? - PullRequest
134 голосов
/ 27 ноября 2010

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

Поскольку в приведенном выше примереможет показаться слишком абстрактным для некоторых, вот более реальный пример того, почему я хочу это сделать.В этом примере я хочу использовать

  • Enums, потому что тогда я могу перечислить конечный набор ключей свойств
  • Generics, потому что тогда у меня может быть безопасность типов на уровне метода для хранениясвойства
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, я не понимаю этого ограничения.

Кто может мне это объяснить?Прежде чем ответить, рассмотрим следующее:

  • Я знаю, что универсальные типы стерты: -)
  • Я знаю, что есть обходные пути с использованием объектов Class.Это обходные пути.
  • Универсальные типы приводят к приведениям типов, генерируемым компилятором, где это применимо (например, при вызове метода convert ()
  • Универсальный тип будет в перечислении. Следовательно,он ограничен каждым из литералов перечисления, поэтому компилятор будет знать, какой тип применять при записи чего-то вроде String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
  • То же самое относится к параметру универсального типа в методе T getvalue(). Компиляторможет применять приведение типа при вызове String string = someClass.getValue(LITERAL1)

Ответы [ 7 ]

46 голосов
/ 07 декабря 2016

Это сейчас обсуждается с Расширенными перечислениями JEP-301 . Пример, приведенный в JEP, это именно то, что я искал:

enum Argument<X> { // declares generic enum
   STRING<String>(String.class), 
   INTEGER<Integer>(Integer.class), ... ;

   Class<X> clazz;

   Argument(Class<X> clazz) { this.clazz = clazz; }

   Class<X> getClazz() { return clazz; }
}

Class<String> cs = Argument.STRING.getClazz(); //uses sharper typing of enum constant

К сожалению, JEP все еще борется со значительными проблемами: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html

11 голосов
/ 27 ноября 2010

Ответ на вопрос:

из-за стирания типа

Ни один из этих двух методов невозможен, поскольку тип аргумента удален.

public <T> T getValue(MyEnum<T> param);
public T convert(Object);

Чтобы реализовать эти методы, вы можете построить перечисление следующим образом:

public enum MyEnum {
    LITERAL1(String.class),
    LITERAL2(Integer.class),
    LITERAL3(Object.class);

    private Class<?> clazz;

    private MyEnum(Class<?> clazz) {
      this.clazz = clazz;
    }

    ...

}
4 голосов
/ 11 февраля 2015

В ENUM есть другие методы, которые не будут работать.Что бы MyEnum.values() вернуло?

А как насчет MyEnum.valueOf(String name)?

Для значения value, если вы думаете, что компилятор может сделать универсальный метод, такой как

public static MyEnumvalueOf (String name);

, чтобы назвать его как MyEnum<String> myStringEnum = MyEnum.value("some string property"), это тоже не сработает.Например, что если вы позвоните MyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")?Невозможно реализовать этот метод для правильной работы, например, чтобы выдать исключение или вернуть ноль, когда вы вызываете его как MyEnum.<Int>value("some double property") из-за стирания типа.

4 голосов
/ 24 февраля 2011

Потому что ты не можешь.Шутки в сторону.Это может быть добавлено к спецификации языка.Не былоЭто добавило бы некоторую сложность.Это преимущество стоимости означает, что она не имеет высокого приоритета.

Обновление: в настоящее время добавляется к языку в JEP 301: расширенные перечисления .

1 голос
/ 29 августа 2011

Честно говоря, это кажется скорее решением проблемы в поисках проблемы, чем чем-либо.

Вся цель перечисления java состоит в том, чтобы смоделировать перечисление экземпляров типов, которые имеют сходные свойства таким образом, что обеспечивает согласованность и богатство, превосходящие сопоставимые представления String или Integer.

Возьмите пример перечисления учебника. Это не очень полезно или не соответствует:

public enum Planet<T>{
    Earth<Planet>,
    Venus<String>,
    Mars<Long>
    ...etc.
}

Зачем мне хотеть, чтобы мои разные планеты имели разные преобразования типов? Какую проблему это решает? Оправдывает ли это усложнение языковой семантики? Если мне нужно такое поведение, является ли enum лучшим средством для достижения этого?

Кроме того, как бы вы управляли сложными конверсиями?

для экземпляра

public enum BadIdea<T>{
   INSTANCE1<Long>,
   INSTANCE2<MyComplexClass>;
}

Достаточно просто с String Integer указать имя или порядковый номер. Но дженерики позволят вам поставить любой тип. Как бы вы справились с конвертацией в MyComplexClass? Теперь вы смешиваете две конструкции, заставляя компилятор знать, что существует ограниченное подмножество типов, которые могут быть предоставлены универсальным перечислениям, и вводите дополнительную путаницу в концепцию (Generics), которая, похоже, уже ускользает от многих программистов.

0 голосов
/ 29 августа 2011

Beekue "enum" - это аббревиатура для перечисления. Это просто набор именованных констант, которые стоят вместо порядковых номеров, чтобы сделать код более читабельным.

Я не понимаю, каким может быть предполагаемое значение параметризованной константы.

0 голосов
/ 27 ноября 2010

Я думаю, потому что в основном нельзя перечислить Enums

Где бы вы установили класс T, если бы JVM позволила вам это сделать?

Перечисление - это данные, которые должны быть всегда одинаковыми или, по крайней мере, динамически не изменяться.

new MyEnum <> ()?

Тем не менее, следующий подход может быть полезным

public enum MyEnum{

    LITERAL1("s"),
    LITERAL2("a"),
    LITERAL3(2);

    private Object o;

    private MyEnum(Object o) {
        this.o = o;
    }

    public Object getO() {
        return o;
    }

    public void setO(Object o) {
        this.o = o;
    }   
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...