Есть ли способ генерировать lambda?

#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. Действительно, выглядит так. Я пришел к выводу, что это ограничение анонимных классов: в отличие от обычных классов, они не могут быть обобщены на уровне класса, и поэтому лямбды (которые являются сахаром над анонимными классами) тоже не могут. Пожалуйста, поправьте меня, если я ошибаюсь.