Как написать краткое замыкание для универсального метода? - PullRequest
0 голосов
/ 04 октября 2018

Я хочу написать реализацию функционального неуниверсального интерфейса, который имеет универсальный метод.Реализация должна быть встроенной и краткой.

В качестве упрощенного примера

@FunctionalInterface interface Fn {
    <R> R fn(R arg);
}
public class Scratch {
    Fn id = arg -> arg;
    //Fn nul = arg -> null;
    //Fn requiresNonNull = ...
}

, который дает

/Scratch.java:5: error: incompatible types: invalid functional descriptor for lambda expression
    Fn id = arg -> arg;
            ^
    method <R>(R)R in interface Fn is generic
  where R is a type-variable:
    R extends Object declared in method <R>fn(R)
1 error

(На самом делепараметр будет представлять собой общий интерфейс с методами, которые имеют возвращаемый тип R.)

Существует ли обходной путь без возврата к многословию анонимного внутреннего класса?

Существует, по-видимому, похожий вопрос: " Невозможно преобразовать функциональный интерфейс с помощью универсального метода в лямбда-выражение ", но это связано с использованием параметра типа с именем Integer вместо чего-то обычного, например T и в принятом ответе Джона Скита говорится, что он не знает решения моей проблемы.

Существует также долгое обсуждение " Беспорядок функционального интерфейса ", который не может ответить на этот вопрос,Это не может быть «подробные анонимные внутренние классы здесь лучше», не так ли?

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

После долгих экспериментов и косвенных попыток у меня есть решение.Я был менее успешен с именами.

Вот идея

  • Ни у функционального интерфейса, ни у одного абстрактного метода нет параметра типа.
  • Функциональный интерфейс получаетпотребитель с параметром типа, но с подстановочными знаками в параметре метода.
  • Потребитель - просто часть внутренних объектов, но у него есть этот параметр типа.Он используется для сохранения результата, в то время как выполнение возвращается через включающую функцию.
  • Потребитель сам получает функциональный интерфейс, содержащий фактический экземпляр бизнес-функции, типизированный типом.
  • Существуетметод по умолчанию, который связывает вещи вместе, включая создание потребителя.

Очистить? [риторический]

Таким образом, вместо возможности писать

Fn id = arg -> arg;

мы можем по крайней мере написать

Fn id = q -> q.q(arg -> arg);

, что является лямбда-лямбдаfactory.

Кажется, у нас закончился синтаксис, и мы не можем написать что-то вроде

Fn id = Fn.Consumer::q(arg -> arg); // not valid syntax!

В целом (с основным, чтобы показать, что я не изменяю)

import java.util.concurrent.atomic.*;

@FunctionalInterface interface Fn {
    interface Instance<R> {
        R fn(R arg);
    }
    interface Consumer<R> {
       void q(Instance<R> gn);
    }

    void consume(Consumer<?> consumer);

    default <R> R fn(R arg) {
        AtomicReference<R> result = new AtomicReference<>();
        this.consume((Instance<R> instance) -> { result.set(instance.fn(arg)); });
        return result.get();
    }
}

public interface Scratch {
    Fn id = q -> q.q(arg -> arg);
    Fn nul = q -> q.q(arg -> null);

    public static void main(String[] args) {
        String idStr = id.fn("cruel");
        String nulStr = nul.fn("cruel");

        System.err.println(idStr);
        System.err.println(nulStr);
    }
}

Я не думаю Я воспользовался отсутствием здравого смысла в системе типов.

(Я, вероятно, должен добавить более сложный пример к вопросу, чтобы показать, почему выможет захотеть сделать это.)

0 голосов
/ 04 октября 2018

Общие лямбды не являются законными, но ссылки на общие методы являются.Вы можете немного сократить многословие анонимных классов, создав вместо этого вспомогательные методы:

public class Scratch {
    Fn id = Scratch::id;
    Fn nul = Scratch::nul;
    Fn requiresNotNull = Objects::requireNonNull;

    private static <R> R id(R arg) {
        return arg;
    }

    private static <R> R nul(R arg) {
        return null;
    }
}
...