Как реализовать универсальный интерфейс, где параметр типа — Enum, а какой-то другой тип

Я работаю с интерфейсом, который принимает параметр типа:

public interface Container<T>

Теперь у меня может быть класс как таковой, который его реализует:

public class EnumContainer implements Container<Enum>

Но теперь предположим, что мне нужен Container для перечислений, который реализует интерфейс с именем Position:

public interface Position {
   String getAbbreviation();
   String getDescription();
}

Как я могу определить PositionEnumContainer? Я пробовал это, но это ошибка времени компиляции:

public class PositionEnumContainer implements Container<Enum & Position>

Я не хочу делать PositionEnumContainer универсальным, например:

public class PositionEnumContainer <T extends Enum<T> & Position> implements Container<T>

person user8297969    schedule 21.01.2020    source источник
comment
Почему вы поместили дженерик в Enum<T>?   -  person dpr    schedule 22.01.2020
comment
В последнем фрагменте кода я поставил Enum<T>, чтобы указать, что T является Enum типа T. Enum само по себе является универсальным.   -  person user8297969    schedule 22.01.2020
comment
...public class PositionEnumContainer implements Container<Enum>, Position? Потому что ваш класс реализует интерфейс Container и интерфейс Position? Вот как работают интерфейсы: вы перечисляете каждый интерфейс, реализуемый вашим классом, через запятую.   -  person Mike 'Pomax' Kamermans    schedule 22.01.2020
comment
@Mike 'Pomax' Kamermans - Спасибо за ответ. Я не хочу, чтобы PositionEnumContainer реализовывал Position. Скорее, я хочу, чтобы Container содержал объекты как типа Enum, так и типа Position.   -  person user8297969    schedule 22.01.2020
comment
Тогда мой встречный вопрос: почему? Они не поддаются унификации, поэтому просто хотеть этого недостаточно: это должно иметь смысл. Во что бы то ни стало, создайте class PositionedEnum implements Enum, Position, а затем сделайте что-то, что реализует Container<PositionedEnum>. Обобщение — это определение одной вещи, которая, как вы обещаете, будет использоваться, а не одной из нескольких совершенно несовместимых вещей.   -  person Mike 'Pomax' Kamermans    schedule 22.01.2020


Ответы (1)


Вы можете заставить PositionEnumContainer реализовать Container<Enum<? extends Position>>.

Если класс расширяет Enum<? extends Position>, то это enum class, который также реализует Position, следовательно, PositionEnumContainer будет принимать только объекты, которые относятся как к типу Enum, так и к типу Position. Но, к сожалению, Java об этом не знает, поэтому производящие методы PositionEnumContainer вернут Enum<? extends Position>, который не может быть неявно приведен обратно к Position, и вам придется приводить его самостоятельно или использовать безопасный метод Enums#narrow.

public class Enums {
    @SuppressWarnings("unchecked") // only class E can extend Enum<E>
    public static <E extends Enum<E>> E narrow(Enum<E> obj) {
        return (E) obj;
    }
}

В качестве альтернативы вы можете создать интерфейс PositionEnum, который extends Position и могут быть реализованы только с помощью перечислений, а затем реализовать PositionEnumContainer Container<PositionEnum>. В этом случае каждый ConcretePositionEnum должен будет реализовать PositionEnum<ConcretePositionEnum> вместо Position.

// should only be implemented by enums
// you can verify it with annotation processor
public interface IEnum<E extends Enum<E>> extends Comparable<E> {
    String name();
    int ordinal();
    Class<E> getDeclaringClass();

    @SuppressWarnings("unchecked")
    default E self() { return (E) this; }
}

public interface PositionEnum<E extends Enum<E> & Position> extends IEnum<E>, Position {}
person Bananon    schedule 22.01.2020