是否可以使用可选参数制作通用功能接口?

Is it possible to make a generic functional interface with optional parameter(s)?

我正在尝试在 Java 中创建一个功能接口,使用一个方法(当然)可以采用任何类型的参数和 return 任何数据类型(一个泛型方法)。

这是我目前拥有的:

Calculator.java

public interface Calculator<T> {
    T operation(T n1, T... n2); //this is where the optional parameter comes in
}

Main.java

public static void main(String[] args) {
    Calculator<Integer> addition = (n1, n2) -> n1 + n2; //this gives an error
}

错误说:

bad operand types for binary operator '+'

public interface Calculator<T> {
    T operation(T n1, T .. n2); //this is where the optional parameter comes in
}

错误是由于您试图将运算符 + 应用于整数和整数数组。下面举例

public interface Calculator<T> {
    T operation(T n1, T n2); //this is where the optional parameter comes in
}

可以正常工作,因为您会将运算符 + 应用于两个整数。

如果您想保持相同的界面,则需要将主要代码更改为:

public static void main(String[] args) {
    Calculator<Integer> addition = (n1, n2) -> n1 + Arrays.stream(n2).reduce(0, Integer::sum);
}

Is it possible to create a generic functional interface with optional parameter(s) in Java?

从这个SO Thread可以读到:

varargs could do that (in a way). Other than that, all variables in the declaration of the method must be supplied. If you want a variable to be optional, you can overload the method using a signature which doesn't require the parameter.

话虽如此,您可以做的是:

public interface Calculator<T> {
    T operation(T ...n);
}

这样,方法operation可以接受0、1 ... N个元素甚至null

然后在你的主目录中:

Calculator<Integer> addition = n -> (n == null) ? 0 : 
                                    Arrays.stream(n).reduce(0, Integer::sum);

A 运行 示例:

public class Main {
    public static void main(String[] args) {
        Calculator<Integer> addition = n -> (n == null) ? 0 : Arrays.stream(n).reduce(0, Integer::sum);
        System.out.println(addition.operation(1, 2));
        System.out.println(addition.operation(1));
        System.out.println(addition.operation());
        System.out.println(addition.operation(null));
    }
}

输出:

3 // 1 + 2
1 // 1
0 // empty array 
0 // null

所示,您可以通过处理varargs参数的lambda表达式来实现您的接口,例如与流。但是你也可以为关联操作设计一个接口,除了两个参数版本之外提供默认方法:

interface Calculator<T> {
    T operation(T n1, T n2);
    default T operation(T first, T... args) {
        return Stream.concat(Stream.of(first), Arrays.stream(args))
            .reduce(this::operation).orElseThrow();
    }
}

请注意,方法签名 operation(T first, T... args) 强制至少有一个元素,这允许使用 reduce 没有标识元素或空流的回退值。

你可以像这样使用它

Calculator<Integer> addition = (n1, n2) -> n1 + n2;
Integer sum = addition.operation(1, 2, 3, 4);

但是运算必须是关联的,例如加法、乘法或字符串连接等。如果您想支持非关联运算,这些运算必须使用适当的实现来覆盖 operation(T first, T... args)。覆盖它不适用于 lambda 表达式。另一种方法是拥有一个用于任意计算的接口和一个用于关联操作的专业化:

interface Calculator<T> {
    T operation(T first, T... args);
}
interface AssociativeOp<T> extends Calculator<T> {
    T operation(T n1, T n2);
    @Override
    default T operation(T first, T... args) {
        return Stream.concat(Stream.of(first), Arrays.stream(args))
            .reduce(this::operation).orElseThrow();
    }
}

然后,非关联操作可以通过处理所有参数的 lambda 表达式实现 Calculator<T>,而关联操作可以像这样实现:

Calculator<Integer> addition = (AssociativeOp<Integer>)(n1, n2) -> n1 + n2;

工厂方法可以通过对其参数使用类型推断来消除类型转换的必要性。然而,当我们在这一点上时,我们可以记得已经有一个函数式接口类型用于带有两个参数的操作,示例甚至隐式地使用了这些参数。所以另一种选择是:

interface Calculator<T> {
    T operation(T first, T... args);

    static <U> Calculator<U> associative(BinaryOperator<U> op) {
        return (first,other) -> Stream.concat(Stream.of(first), Arrays.stream(other))
            .reduce(op).orElseThrow();
    }
}

这仍然允许通过 lambda 表达式实现任意实现,而关联操作可以通过简化的方式实现:

Calculator<Integer> addition = Calculator.associative((n1, n2) -> n1 + n2);

(或Calculator.associative(Integer::sum))