#java #generics #lambda
#java #дженерики #лямбда
Вопрос:
Я пытаюсь написать лямбда-выражение, которое будет реализовывать a @FunctionalInterface
( BiFunction
в этом примере) и будет возвращать объект, сгенерированный с <T>
помощью .
Я могу сделать это с помощью старого подхода, объявляющего обобщенный класс (см. class Extractor<T>
), но я не понимаю, как это сделать с помощью lambda. Единственное, что я могу сделать, это объявить лямбду с «неизвестным» <?>
универсальным типом.
По сути, я хочу объявить лямбда-выражение, к которому можно перейти method2
. Возможно ли это?
private BiFunction<String, Root<Employee>, Path<?>> extractorLambda =
(fieldName, root) -> root.get(fieldName);
class ExtractorClass<T> implements BiFunction<String, Root<Employee>, Path<T>> {
@Override
public Path<T> apply(final String fieldName, final Root<Employee> root) {
return root.get(fieldName);
}
}
private Specification<Employee> method1(String fieldName,
BiFunction<String, Root<Employee>, Path<?>> extractor) {
return null;
}
private <T> Specification<Employee> method2(String fieldName,
BiFunction<String, Root<Employee>, Path<T>> extractor) {
return null;
}
public void main(String[] args) {
method1("s", extractorLambda); // ok
method2("s", extractorLambda); // error
ExtractorClass<String> extractorClass = new Extractor<>();
method2("s", extractorClass); // ok
}
Комментарии:
1.
Extractor<ABC> extractor = (fieldName, root) -> root.get(fieldName);
2. Разве тип
EXTRACTOR
не должен бытьExtractor<String>
, вместоBiFunction<String, Root<Employee>, Path<?>>
?3. Тогда нет смысла иметь лямбду, потому что тогда мне нужно объявить
Extractor<T>
класс. Я декализировал его здесь только для того, чтобы показать, что можно генерировать на «уровне класса» с помощью <T> , но как добиться того же, объявив только лямбда-код, который реализует бифункцию?4. Вы можете создать
Extractor
интерфейс и расширитьBiFunction
его, тогда его можно было бы использовать как цель лямбда-выражения. Тем не менее, мне трудно понять цельEXTRACTOR
. Если у вас есть метод, который заботится об общем типеPath
, почему вы хотите использовать синглтон, в котором указано, что общий типPath
неизвестен?5. @Slaw , вероятно, мой вопрос не очень понятен, так как все путаются в этом
Extractor
классе, но чего я хочу добиться, так это сделать так, чтобы лямбда была специально обобщена с<T>
помощью вместо<?>
(неважно, является ли она статической или окончательной) точно так же, как класс Extractor . По сути, я хочу объявить лямбду, которая будет переданаmethod2
Ответ №1:
Проблема заключается в разнице между <T>
и <?>
.
Ваш метод2 ожидает список объектов одного типа. <?>
это неограниченный подстановочный знак, который означает, что он может содержать все, что угодно, если оно расширяет объект.
То же самое относится и в другом направлении. Если вы попытаетесь использовать свой класс Extractor с method1, он не будет компилироваться, потому что class говорит, что он работает только для списков определенного типа.
Насколько я знаю, у вас не может быть определения универсального типа в поле. Основываясь на вашем комментарии, это выглядит как кратчайший обходной путь:
private interface E2<T> extends BiFunction<String, Root<Employee>, Path<T>>{}
private static final E2 EXTRACTOR = (fieldName, root) -> root.get(fieldName);
Комментарии:
1. Хорошо, я понял, но могу ли я переработать свою лямбду, чтобы она соответствовала
method2
?2. Я обновил свой ответ. Из того, что я знаю, вы в конечном итоге будете использовать старый подход. Ну, чего мы могли бы ожидать, когда лямбды — это просто синтаксический сахар выше старого подхода.
3. Действительно, выглядит так. Я пришел к выводу, что это ограничение анонимных классов: в отличие от обычных классов, они не могут быть обобщены на уровне класса, и поэтому лямбды (которые являются сахаром над анонимными классами) тоже не могут. Пожалуйста, поправьте меня, если я ошибаюсь.