Что было бы иначе в Java, если бы в объявлении Enum не было рекурсивной части - PullRequest
14 голосов
/ 18 июня 2010

Пожалуйста, смотрите Определение Java Enum а также Почему в java enum объявлен как Enum > для общего обсуждения. Здесь я хотел бы узнать, что именно было бы сломано (больше не типизировано, не требует дополнительных приведений и т.д.), если класс Enum был определен как

public class Enum<E extends Enum> 

Я использую этот код для проверки своих идей:

interface MyComparable<T> {
    int myCompare(T o);
}

class MyEnum<E extends MyEnum> implements MyComparable<E> {
    public int myCompare(E o) { return -1; }
}

class FirstEnum extends MyEnum<FirstEnum> {}

class SecondEnum extends MyEnum<SecondEnum> {}

С его помощью я не смог найти никаких преимуществ в этом конкретном случае.

PS. тот факт, что мне не разрешено делать

class ThirdEnum extends MyEnum<SecondEnum> {}

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

PPS. Я все больше склоняюсь к тому, что правильный ответ здесь будет «ничего не изменится, если вы удалите рекурсивную часть» - но я просто не могу в это поверить.

Ответы [ 5 ]

2 голосов
/ 18 июня 2010

Ну, во-первых, он будет жаловаться на использование необработанного типа, но вы можете сделать:

public class Enum<E extends Enum<?>>

для того же эффекта.может сделать что-то вроде:

class FirstEnum extends MyEnum<SecondEnum> {
}

class SecondEnum extends MyEnum<FirstEnum> {
}

, что мне кажется, что это может привести к большим неприятностям.Точнее, вы не можете сравнивать перечисление типа FirstEnum с перечислением того же типа, вы должны сравнить его с перечислением другого типа, что очень неприятно, если у вас есть List<FirstEnum>, который вы хотите отсортировать.Пример не будет компилироваться, если я установлю E вместо o ?, поскольку SecondEnum не относится к типу E extends MyEnum<E> (это приведет к циклическому наследованию).Хотя это будет работать, если FirstEnum extends MyEnum<FirstEnum> (это означает, что SecondEnum является дочерним классом FirstEnum - нормальное иерархическое наследование).

1 голос
/ 18 июня 2010

Я считаю, что убедительная причина сделать это - сделать код в классе MyEnum более безопасным.

Учтите, что рекурсивная часть делает такую ​​вещь возможной:

class MyEnum<E extends MyEnum<E>> {
    private Thing<E> util1() { return someObject }
    private void util2(E e) {}
    public int method(E o) { 
        Thing<E> thingy = o.util1(); 
        // You can call the util1 method on o and get a type safe return element.
        E o1 = // I don't care how you get a parametrized E object.
        o.util2(o1);
        // You can call the util2 method with a typesafe parameter.
    }
}

Короче говоря, эта рекурсивность позволяет помещать безопасные с точки зрения типов методы в класс Enum, который можно вызывать для любого элемента E, и эти вызовы будут безопасными с точки зрения типов.

1 голос
/ 18 июня 2010

Рассмотрим Enum<E>.compareTo(E other).

Это:

  • Нужно работать с E, а не с перечислением, чтобы не пытаться сравнивать одно значение перечисления со значением из другого перечисления
  • Необходимо иметь возможность получить порядковый номер перечисления с помощью метода ordinal() , объявленного в Enum.

Как бы вы предложили сделать эту работу без текущего ограничения?

Это только первый, который я придумал ... Я уверен, что есть много других. По сути, это способ сказать: «Вы не должны пытаться рассматривать все перечисления как эквивалентные друг другу ... перечисление само по себе является замкнутым множеством, но все перечисления имеют определенные свойства». *

0 голосов
/ 18 июня 2010
`X extends Enum<Y>`

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

0 голосов
/ 18 июня 2010

Если у вас нет параметра универсального типа, вы не сможете расширить Comparable<T extends Comparable> для определенного подтипа enum, который вы создали.

Вы можете игнорировать это и создать свой собственный тип MyEnum, который ведет себя примерно одинаково, но без ограничения, что разные MyEnum не сравнимы:

public abstract class MyEnum implements Comparable<MyEnum>
{
    private final int ordinal;

    protected MyEnum ( int ordinal )
    {
        this.ordinal = ordinal;
    }

    public int ordinal ()
    {
        return ordinal ;
    }

    public int compareTo ( MyEnum other )
    {
        return ordinal - other.ordinal; // ignore overflow for now
    }

    public boolean equals (Object other) {
        return ordinal == ((MyEnum)other).ordinal;
    }

    public int hashCode () {
        return ordinal;
    }
}

Это ведет себя во многом так же, как перечисление для определенных операций, но вместо того, чтобы быть безопасной реализацией универсального типа, подчиняется LSP - объекты разных подклассов MyEnum сравнимы или равны друг другу если они имеют одинаковое порядковое значение.

public static class EnumA extends MyEnum
{
    private EnumA ( int ordinal ) { super ( ordinal ); }
    public static final EnumA a = new EnumA ( 0 );
    public static final EnumA b = new EnumA ( 1 );
}

public static class EnumB extends MyEnum
{
    private EnumB ( int ordinal ) { super ( ordinal ); }
    public static final EnumB x = new EnumB ( 0 );
    public static final EnumB y = new EnumB ( 1 );
}

public static void main ( String...args )
{
    System.out.println ( EnumA.a.compareTo ( EnumB.x ) );
    System.out.println ( EnumA.a.equals ( EnumB.x ) );
    System.out.println ( EnumA.a.compareTo ( EnumB.y ) );
    System.out.println ( EnumA.a.equals ( EnumB.y ) );
}

В этом случае, если вы не переопределите equals, вы потеряете соглашение, которое x.comparesTo(y)=0 подразумевает x.equals(y); если вы переопределяете equals, то в некоторых случаях x.equals(y) не подразумевает x == y (как для других объектов-значений), тогда как для перечислений Java оба теста на равенство дают один и тот же результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...