Java ссылается на тип Enum в определении анонимного класса
Я пытаюсь определить следующий анонимный экземпляр функции Google Guava:
Function<E extends Enum<E>, String> ENUM_TO_STRING = new Function<E extends Enum<E>, String>() {
@Override
public String apply(E enumValue) {
String result = null;
if (enumValue != null) {
result = enumValue.toString();
}
return result;
}
};
Идея заключается в простом экземпляре функции преобразования с методом apply, который вызывает toString для любого значения перечисления, если оно не равно нулю, или возвращает ноль, если значение равно нулю.
К сожалению, компилятор жалуется на общую ссылку E, которая расширяет Enum. Я знаю, что это выражение может использоваться в неанонимных контекстах, например:
public static <E extends Enum<E>> String enumToString(E enumValue) {
String result = null;
if (enumValue != null) {
result = enumValue.toString();
}
return result;
}
Первое, анонимное определение класса не в порядке. Второе определение неанонимного метода - ОК.
Есть ли способ определить анонимный класс с помощью Enums?
Спасибо
3 ответа
Как указывает Сотириос, параметры универсального типа могут быть объявлены только для классов или методов, но не для полей. На самом деле, декларируя E
вероятно, не нужно - а Function<Enum<?>, String>
было бы хорошо:
Function<Enum<?>, String> ENUM_TO_STRING = new Function<Enum<?>, String>() {
@Override
public String apply(Enum<?> enumValue) {
if (enumValue != null) {
return enumValue.toString();
}
return null;
}
};
Следуя этой мысли, toString
объявлен Object
не Enum
итак генерал Function<Object, String>
имеет больше смысла:
Function<Object, String> NULLABLE_TO_STRING = new Function<Object, String>() {
@Override
public String apply(Object obj) {
if (obj != null) {
return obj.toString();
}
return null;
}
};
В общем, лучше скрывать детали реализации за фабричными методами, облегчая будущие изменения, например:
private static final Function<Object, String> NULLABLE_TO_STRING = ...
public static Function<Object, String> nullableToString() {
return NULLABLE_TO_STRING;
}
Обратите внимание, что Function<Object, String>
все еще возвращается. Это должно быть хорошо, если ваши API используют PECS (например, принимают Function<? super Foo, ? extends Bar>
), но мы также можем пойти дальше и предложить конкретный тип ввода, сделав метод универсальным:
private static final Function<Object, String> NULLABLE_TO_STRING = ...
public static <T> Function<T, String> nullableToString() {
@SuppressWarnings("unchecked") // safe contravariant cast
final Function<T, String> withNarrowedType =
(Function<T, String>)(Function<?, String>)NULLABLE_TO_STRING;
return withNarrowedType;
}
Обратите внимание, что это использует тот же Function
экземпляр, с непроверенным приведением, чтобы сузить его тип ввода. Актерский состав безопасен, потому что NULLABLE_TO_STRING
принимает любой объект и не имеет состояния. Этот паттерн демонстрируется в пункте 27 "Эффективного Java" Джошуа Блоха "Пользуйся универсальными методами".
Вы не можете просто использовать переменные универсального типа в любом месте. Вы должны быть в контексте, где они могут быть объявлены и доступны.
Во втором примере они объявлены в сигнатуре метода.
В вашем первом примере это не похоже на E
объявляется где угодно. Другими словами, вы не можете объявить переменную типа как часть объявления переменной. Вы можете сделать то, что вы пытаетесь, если метод или класс, в котором этот фрагмент кода появился в объявленной переменной типа E
сам.
Вам, вероятно, здесь вообще не нужны универсальные шаблоны, поскольку вы фактически не используете информацию о типе, и она все равно теряется во время выполнения. Сначала начните с применения функции старой школы (Enum enumValue). Если вы действительно не хотите подавлять предупреждения и нуждаетесь в проверке типа, вы можете продолжить расследование.