Почему в java перечисление объявляется как Enum <E extends Enum >

Возможный дубликат:
определение java Enum

Лучше сформулированный вопрос, который не считается дубликатом:
Что было бы иначе в Java, если декларация Enum не имела рекурсивной части

если бы разработчики языка просто использовали Enum , как это повлияет на язык?

Единственная разница теперь в том, что кто-то напишет написать

 A расширяет Enum 

но поскольку в java не разрешено расширять enums, которые будут по-прежнему незаконными. Я также думал о том, что кто-то снабжает jvm байт-кодом, который определяет smth как расширение enums, но generics не могут повлиять на это, поскольку все они стираются.

Так в чем же смысл такой декларации?

Спасибо!

Редактирование для простоты давайте посмотрим на пример:

 интерфейс MyComparable  {
     int myCompare (T o);
 }

 class MyEnum  реализует MyComparable  {
     public int myCompare (E o) {return -1;  }
 }

 class FirstEnum extends MyEnum  {}

 class SecondEnum extends MyEnum  {}

что случилось с этой classовой структурой? Что можно сделать, чтобы «MyEnum <E extends MyEnum >» ограничивал?

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

Короткий ответ заключается в том, что он заставляет class параметризуться сам по себе; это необходимо для того, чтобы суперclassы определяли методы, используя общий параметр, которые работают прозрачно («изначально», если хотите) с их подclassами.

Редактировать: как пример (не), например, рассмотрите метод clone() Object . В настоящее время он определен как возвращающее значение типа Object . Благодаря ковариантным типам возврата определенные подclassы могут (и часто делают) определять, что они возвращают более специфический class, но это не может быть применено и, следовательно, не может быть выведено для произвольного classа.

Теперь, если Object был определен как Enum, то есть Object> тогда вам нужно будет определить все classы как нечто вроде public class MyFoo . Следовательно, clone() может быть объявлен для возврата типа T и во время компиляции вы можете обеспечить, чтобы возвращаемое значение всегда было точно таким же classом, что и сам объект (даже подclassы не соответствовали параметрам).

Теперь в этом случае Object не параметризуется так, потому что было бы крайне неприятно иметь этот багаж во всех classах, когда 99% из них не собираются его использовать вообще. Но для некоторых иерархий classов это может быть очень полезно – я уже сам использовал подобную технику с типами абстрактных, рекурсивных парсеров выражений с несколькими реализациями. Эта конструкция позволила написать код, который был «очевидным», без необходимости бросать всюду, или копировать и вставлять только для изменения конкретных определений classов.

Изменить 2 (Чтобы ответить на ваш вопрос!):

Если Enum был определен как Enum , то, как вы правильно говорите, кто-то может определить class, поскольку A extends Enum . Это побеждает точку общей конструкции, которая заключается в обеспечении того, что общий параметр всегда является точным типом рассматриваемого classа. Приведя конкретный пример, Enum объявляет свой метод compareTo как

 public final int compareTo(E o) 

В этом случае, поскольку вы определили A для расширения Enum , экземпляры A можно сравнить только с экземплярами B (независимо от B), что почти наверняка не очень полезно. С дополнительной конструкцией вы знаете, что любой class, который расширяет Enum, сопоставим только с самим собой. И, следовательно, вы можете обеспечить реализации методов в суперclassе, которые остаются полезными и конкретными во всех подclassах.

(Без этого рекурсивного трюка generics, единственным другим вариантом было бы определить compareTo как public final int compareTo(Enum o) . Это не совсем то же самое, что и тогда можно сравнить java.math.RoundingMode с java.lang.Thread.State без компилятора, который снова не очень полезен.)


Хорошо, давайте Enum от самого Enum , поскольку мы, кажется, повесили трубку. Вместо этого вот абстрактный class:

 public abstract class Manipulator> { /** * This method actually does the work, whatever that is */ public abstract void manipulate(DomainObject o); /** * This creates a child that can be used for divide and conquer-y stuff */ public T createChild() { // Some really useful implementation here based on // state contained in this class } } 

У нас будет несколько конкретных реализаций этого – SaveToDatabaseManipulator, SpellCheckingManipulator, что угодно. Кроме того, мы также хотим, чтобы люди определяли свои собственные, так как это супер-полезный class. 😉

Теперь вы заметите, что мы используем рекурсивное общее определение, а затем возвращаем T из метода createChild . Это значит, что:

1) Мы знаем, и компилятор знает, что если я позвоню:

 SpellCheckingManipulator obj = ...; // We have a reference somehow return obj.createChild(); 

то возвращаемое значение, безусловно, является SpellCheckingManipulator , хотя оно использует определение из суперclassа. Рекурсивные дженерики позволяют компилятору знать, что очевидно для нас, поэтому вам не нужно оставлять возвращаемые значения (например, часто вам приходится делать clone() ).

2) Обратите внимание, что я не объявлял метод final, так как, возможно, некоторые подclassы захотят переопределить его с более подходящей версией для себя. Определение дженериков означает, что независимо от того, кто создает новый class или как он определяется, мы все же можем утверждать, что возврат из, например, BrandNewSloppilyCodedManipulator.createChild() все равно будет экземпляром BrandNewSloppilyCodedManipulator . Если небрежный разработчик пытается определить его, чтобы вернуть только Manipulator , компилятор не допустит их. И если они попытаются определить class как BrandNewSloppilyCodedManipulator , это тоже не позволит им.

В принципе, вывод заключается в том, что этот трюк полезен, когда вы хотите предоставить некоторую функциональность в суперclassе, который каким-то образом становится более конкретным в подclassах. Объявив суперclass таким образом, вы блокируете общий параметр для любых подclassов как самого подclassа. Вот почему вы можете написать общий метод compareTo или createChild в суперclassе и не допустить его чрезмерной неопределенности, когда вы имеете дело с определенными подclassами.

Единственная разница теперь в том, что кто-то напишет написать

 A extends Enum 

но поскольку в java не разрешено расширять enums, которые будут по-прежнему незаконными. Я также думал о том, что кто-то снабжает jvm байт-кодом, который определяет smth как расширение enums, но generics не могут повлиять на это, поскольку все они стираются.

Вы правы, что язык помешает кому-то сделать это. Однако преимущество сделать это E extends Enum а не просто extends Enum это то, что некоторые из методов Enum вернут правильный конкретный тип.

Плакат попросил что-то, что не сработает. Если Enum был определен как Enum , вы могли бы сделать это:

 // Would be legal, because element types of `DaysOfWeek` and `Color` both // extend `Enum`, not `Enum>`. DaysOfWeek d = Enum.valueOf(Color.class, "Purple"); 
  • Архитектура Java EE. Являются ли DAO по-прежнему рекомендуемыми при использовании ORM, таких как JPA 2?
  • Что со статической памятью в java?
  • jackson: Что произойдет, если имущество отсутствует?
  • Есть ли способ получить идентификатор автоинкремента из подготовленного оператора
  • Подсчитайте количество появлений слова в строке
  • Это плохая практика, чтобы поймать Throwable?
  • Исключение с одновременной модификацией
  • Как «правильно» обнаружить DPI дисплея с Java?
  • Ошибка памяти в Hadoop
  • Генерики, массивы и ClassCastException
  • Пожалуйста, объясните: insertable = false, updatable = false
  • Давайте будем гением компьютера.