Использование интерфейсных методов перечисления в методе, принимающем класс
У меня есть метод, который должен принять класс 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-дженериков, которые хорошо или никогда не использовали множественные границы (я часто использую дженерики, но мне никогда не нужна такая функция, поэтому я немного отключался, когда я видел).