Использование интерфейсных методов перечисления в методе, принимающем класс

У меня есть метод, который должен принять класс Enum. Эти перечисления реализуют интерфейс. Теперь мне нужен доступ к методам Enum, таким как ordinal(), name() и т.д. И мои методы интерфейса. Что я пробовал:

public <T extends ConfigFeature, Enum> void showEnabledFeatures(Class<T> enumType, long mask) {
    List<T> list = Arrays.asList(enumType.getEnumConstants());
    list.forEach(item -> {
        // My interface method, works fine
        item.getMask();
        // Enum method doesn't work:
        // item.ordinal();
    });
}

Обратный порядок отменяет работу:

public <T extends Enum, ConfigFeature> void showEnabledFeatures(Class<T> enumType, long mask) {
    List<T> list = Arrays.asList(enumType.getEnumConstants());
    list.forEach(item -> {
        // My interface method, doesn't work now
        // item.getMask();
        // Enum method now works:
        item.ordinal();
    });
}

Есть ли способ получить доступ к обоим методам из интерфейса и Enum?

Ответ 1

Вы используете неправильный синтаксис, чтобы сказать, что T должен реализовать этот интерфейс и является перечислением.

Это:

<T extends ConfigFeature, Enum>

не ограничивает T до Enum, но фактически создает новый общий параметр Enum.

Так же,

<T extends Enum, ConfigFeature>

не ограничивает T ConfigFeature. Вы объявляете новый общий параметр ConfigFeature.

Правильный синтаксис заключается в использовании &:

<T extends Enum<T> & ConfigFeature>

Обратите внимание, что порядок действительно важен здесь! Enum может наступить только первым.

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

TypeParameter:
    {TypeParameterModifier} Identifier [TypeBound]

TypeParameterModifier:
    Annotation

TypeBound:
    extends TypeVariable
    extends ClassOrInterfaceType {AdditionalBound}

AdditionalBound:
    & InterfaceType

Ответ 2

Ваш синтаксис неверен; тебе нужно:

public <T extends Enum<T> & ConfigFeature>

Этот синтаксис, который вы использовали, создает два типичных типа, один из которых называется T а один называется Enum (где Enum не ограничен, а T ограничено расширением ConfigFeature).

Обратите внимание, что во избежание любых предупреждений о генераторах об использовании типов raw вам также необходимо предоставить параметр типа для привязки Enum. Перечисление, называемое X всегда расширяет Enum<X>, поэтому вы можете использовать T extends Enum<T>, а полный текст локального обобщенного объявления метода становится <T extends Enum<T> & ConfigFeature>

Ответ 3

Заменить , в вашем втором примере с &. Вы можете использовать & для объявления нескольких границ при условии, что интерфейсы theyre от второго типа и далее. Если вы используете запятую, это отдельный параметр типа, а не связанный.

Ответ 4

Чтобы добавить к существующим ответам, вместо использования Mutliple Bounds, как описано в других ответах, вы можете определить интерфейс, который сочетает в себе интерфейс с методом return для перечисления, например:

public interface ConfigFeatureEnumI <T extends Enum<T>> extends ConfigFeatureI{
    @SuppressWarnings("unchecked")
    default public T asEnum() {
        return (T) this;
    }
}

Вы можете реализовать asEnum() в используемом enum или просто использовать метод по умолчанию, если Java 8 доступен, как я покажу здесь.

public enum ConfigEnum implements ConfigFeatureEnumI<ConfigEnum>{//...

Тогда showEnabledFeatures может выглядеть так:

public <T extends ConfigFeatureEnumI<?>> void showEnabledFeatures(Class<T> enumType, long mask) {
    List<T> list = Arrays.asList(enumType.getEnumConstants());
    list.forEach(item -> {
        // Interface method works:
        item.getMask();
        // Enum method works via asEnum():
        item.asEnum().ordinal();
    });
}

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