Определение в Java перечислимые



Я думал, что понимаю Java generics довольно хорошо, но потом я наткнулся на следующее в java.ленг.Перечисление:



class Enum<E extends Enum<E>>


может кто-нибудь объяснить, как интерпретировать этот параметр типа? Бонусные баллы за предоставление других примеров использования аналогичного параметра типа.

557   7  

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 возвращает a Node Так что цепочка не будет работать для 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, это бесполезно. Все будет работать так же, если он был объявлен как

class Enum<E>

если вы посмотрите на 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

    Ничего не найдено.