Определение в Java перечислимые
Я думал, что понимаю Java generics довольно хорошо, но потом я наткнулся на следующее в java.ленг.Перечисление:
class Enum<E extends Enum<E>>
может кто-нибудь объяснить, как интерпретировать этот параметр типа? Бонусные баллы за предоставление других примеров использования аналогичного параметра типа.
7 ответов:
это означает, что аргумент типа для перечисления должен быть производным от перечисления, которое само имеет тот же аргумент типа. Как такое могло случиться? Сделав аргумент типа самим новым типом. Поэтому, если у меня есть перечисление под названием StatusCode, это будет эквивалентно:
public class StatusCode extends Enum<StatusCode>теперь, если вы проверите ограничения, у нас есть
Enum<StatusCode>- такE=StatusCode. Давайте проверим: делаетEрасширенияEnum<StatusCode>? Да! Мы в порядке.вы вполне можете спросить себя, в чем смысл этого :) Ну, это означает, что API для перечисления может ссылаться на себя - например, будучи в состоянии сказать, что
Enum<E>осуществляетComparable<E>. Базовый класс может делать сравнения (в случае перечисления), но он может убедиться, что он сравнивает только правильный вид перечислений друг с другом. (EDIT: ну, почти-см. редактирование внизу.)я использовал что-то подобное в моем порту C# ProtocolBuffers. Есть "сообщения" (неизменяемые) и "строители" (изменяемые, используемые для построения сообщения) - и они приходят как пары типов. Задействованные интерфейсы:
public interface IBuilder<TMessage, TBuilder> where TMessage : IMessage<TMessage, TBuilder> where TBuilder : IBuilder<TMessage, TBuilder> public interface IMessage<TMessage, TBuilder> where TMessage : IMessage<TMessage, TBuilder> where TBuilder : IBuilder<TMessage, TBuilder>это означает, что из сообщения Вы можете получить соответствующий конструктор (например, взять копию сообщения и изменить некоторые биты), а из конструктора вы можете получить соответствующее сообщение, когда закончите его создание. Это хорошая работа, пользователи API не должны на самом деле заботиться об этом, хотя - это ужасно сложно, и потребовалось несколько итераций, чтобы добраться туда, где он находится.
EDIT: обратите внимание, что это не мешает вам создавать нечетные типы, которые используют аргумент типа, который сам по себе в порядке, но который не является одним и тем же типом. Цель состоит в том, чтобы дать преимущества в право дело, а не защитить вас от неправильно случае.
так что если
Enumне обрабатывались "специально" в Java в любом случае, вы могли бы (как отмечено в комментариях) создать следующие типы:public class First extends Enum<First> {} public class Second extends Enum<First> {}
SecondреализоватьComparable<First>, а неComparable<Second>... ноFirstсамо по себе было бы прекрасно.
ниже приводится модифицированная версия объяснения из книги Java Generics and Collections: У нас есть
Enumобъявилenum Season { WINTER, SPRING, SUMMER, FALL }который будет расширен до класса
final class Season extends ...здесь
...должен быть каким-то параметризованным базовым классом для перечислений. Давайте работать что это должно быть. Ну и одно из требований кSeasonэто то, что он должен реализоватьComparable<Season>. Так что мы собираемся нужноSeason extends ... implements Comparable<Season>что вы могли бы использовать для
...что позволит этому работать? Учитывая, что это должна быть параметризацияEnum, единственный выборEnum<Season>, так что вы можете иметь:Season extends Enum<Season> Enum<Season> implements Comparable<Season>так
Enumпараметризуется на таких типах, какSeason. Аннотация изSeasonи вы получаете, что параметрEnum- это любой тип, который удовлетворяетE extends Enum<E>
Морис нафталин (соавтор, Java Generics and Collections)
это можно проиллюстрировать простым примером и методом, который может быть использован для реализации цепных вызовов методов для подклассов. В примере ниже
setNameвозвращает aNodeТак что цепочка не будет работать дляCity:class Node { String name; Node setName(String name) { this.name = name; return this; } } class City extends Node { int square; City setSquare(int square) { this.square = square; return this; } } public static void main(String[] args) { City city = new City() .setName("LA") .setSquare(100); // won't compile, setName() returns Node }таким образом, мы могли бы ссылаться на подкласс в общем объявлении, так что
Cityтеперь возвращает правильный тип модели:abstract class Node<SELF extends Node<SELF>>{ String name; SELF setName(String name) { this.name = name; return self(); } protected abstract SELF self(); } class City extends Node<City> { int square; City setSquare(int square) { this.square = square; return self(); } @Override protected City self() { return this; } public static void main(String[] args) { City city = new City() .setName("LA") .setSquare(100); // ok! } }
вы не единственный, кто задается вопросом, что это значит; см. хаотический блог Java.
"Если класс расширяет этот класс, он должен передать параметр E. границы параметра E предназначены для класса, который расширяет этот класс с тем же параметром E".
этот пост полностью прояснил для меня эту проблему "рекурсивных общих типов". Я просто хотел добавить еще один случай, где эта структура необходима.
предположим, что у вас есть общие узлы в общем графе:
public abstract class Node<T extends Node<T>> { public void addNeighbor(T); public void addNeighbors(Collection<? extends T> nodes); public Collection<T> getNeighbor(); }тогда вы можете иметь графики специализированных типов:
public class City extends Node<City> { public void addNeighbor(City){...} public void addNeighbors(Collection<? extends City> nodes){...} public Collection<City> getNeighbor(){...} }
если вы посмотрите на
Enumисходный код, он имеет следующий вид:public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { public final int compareTo(E o) { Enum<?> other = (Enum<?>)o; Enum<E> self = this; if (self.getClass() != other.getClass() && // optimization self.getDeclaringClass() != other.getDeclaringClass()) throw new ClassCastException(); return self.ordinal - other.ordinal; } @SuppressWarnings("unchecked") public final Class<E> getDeclaringClass() { Class<?> clazz = getClass(); Class<?> zuper = clazz.getSuperclass(); return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper; } public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { T result = enumType.enumConstantDirectory().get(name); if (result != null) return result; if (name == null) throw new NullPointerException("Name is null"); throw new IllegalArgumentException( "No enum constant " + enumType.getCanonicalName() + "." + name); } }во-первых, что делает
E extends Enum<E>в смысле? Это означает, что параметр типа-это то, что простирается от перечисления и не параметризуется с помощью необработанного типа (он параметризуется сам по себе).это актуально, если у вас есть перечислимый
public enum MyEnum { THING1, THING2; }который, если я правильно знаю, переводится как
public final class MyEnum extends Enum<MyEnum> { public static final MyEnum THING1 = new MyEnum(); public static final MyEnum THING2 = new MyEnum(); }таким образом, это означает, что MyEnum получает следующее методы:
public final int compareTo(MyEnum o) { Enum<?> other = (Enum<?>)o; Enum<MyEnum> self = this; if (self.getClass() != other.getClass() && // optimization self.getDeclaringClass() != other.getDeclaringClass()) throw new ClassCastException(); return self.ordinal - other.ordinal; }и что еще более важно,
@SuppressWarnings("unchecked") public final Class<MyEnum> getDeclaringClass() { Class<?> clazz = getClass(); Class<?> zuper = clazz.getSuperclass(); return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper; }это делает
getDeclaringClass()приведите к правильному
Comments