如何为通用方法编写简洁的闭包?

时间:2018-10-03 23:23:13

标签: java lambda generic-method generic-lambda

我想编写一个具有泛型方法的功能非泛型接口的实现。该实现必须是内联的,简洁的。

作为简化的示例

@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的方法的通用接口。)

是否有一种变通方法,而无需回到匿名内部类的冗长程度?

显然有一个类似的问题“ Cannot convert functional interface with generic method into lambda expression”,但这源于使用名为Integer的类型参数,而不是诸如T这样的常规参数,乔恩·斯凯特(Jon Skeet)接受的答案说他不知道我的问题的解决方案。

还有很长的讨论“ Functional interface confusion”,未能回答这个问题。不能是“最好的详细匿名内部类在这里”,可以吗?

2 个答案:

答案 0 :(得分:1)

经过大量的实验和间接的尝试后,我有了一个解决方案。我的命名不太成功。

这是主意

  • 功能接口和单一抽象方法都没有类型参数。
  • 功能接口接收带有类型参数但使用方法参数通配符的使用者。
  • 使用者只是一个内部gubbins,但它确实具有该类型参数。当执行通过封闭函数返回时,它用于存储结果。
  • 消费者本身会收到一个功能接口,其中包含类型为参数化类型的实际业务功能实例。
  • 有一个默认方法可以将事物联系在一起,包括创建使用者。

清除? [修辞]

所以不能写

Fn id = arg -> arg;

我们至少可以写

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

这是lambda lambda工厂。

我们似乎语法用尽了,不能写类似的东西

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);
    }
}

我不认为我已经利用了类型系统中缺乏任何健全性。

(我可能应该在问题中添加一个更复杂的示例,以说明您为什么要这样做。)

答案 1 :(得分:0)

泛型lambda无效,但泛型方法引用合法。您可以通过创建辅助方法来减少匿名类的冗长程度:

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;
    }
}