简体   繁体   English

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

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

I'm trying to create a functional interface in Java with a single method (of course) that can take any type of parameter(s) and return any data type ( ie a generic method).我正在尝试在 Java 中使用一种方法(当然)创建一个功能接口,该方法可以采用任何类型的参数并返回任何数据类型(通用方法)。

This is what I have so far:这是我到目前为止所拥有的:

Calculator.java计算器.java

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

Main.java主.java

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

The error says:错误说:

bad operand types for binary operator '+'二元运算符“+”的错误操作数类型

  • Is it possible to create a generic functional interface with optional parameter(s) in Java?是否可以在 Java 中创建带有可选参数的通用功能接口?
  • If so, what am I doing wrong?如果是这样,我做错了什么?
public interface Calculator<T> {
    T operation(T n1, T .. n2); //this is where the optional parameter comes in
}

The error comes from the fact that you are trying to apply the operator + to a Integer and an Array of Integers.该错误来自您尝试将运算符+应用于 Integer 和整数数组的事实。 The following, for instance以下列为例

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

would work fine, since you would be applying the operator + to two Integers.可以正常工作,因为您会将运算符+应用于两个整数。

If you want to keep the same Interface then you need to change you code in the main to:如果要保持相同的接口,则需要将主代码更改为:

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?是否可以在 Java 中创建带有可选参数的通用功能接口?

From this SO Thread one can read:从这个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.如果您希望变量是可选的,您可以使用不需要参数的签名重载该方法。

That being said, what you could do is something like:话虽这么说,你可以做的是:

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

In this way, the method operation can accept 0, 1... N elements and even null .这样,方法operation可以接受 0、1...N 个元素甚至null

Then in your main:然后在你的主要:

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

A running Example:运行示例:

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

Output: Output:

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

As shown in this answer , you can implement your interface by lambda expressions processing the varargs parameters, eg with a Stream.this answer所示,您可以通过处理可变参数参数的 lambda 表达式来实现您的接口,例如使用 Stream。 But you can also design an interface for associative operations providing default methods in addition to the two argument version:但是您也可以为关联操作设计一个接口,除了两个参数版本之外还提供默认方法:

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

Note that the method signature operation(T first, T... args) enforces that there is at least one element, which allows to use reduce without an identity element or fallback value for empty streams.请注意,方法签名operation(T first, T... args)强制至少存在一个元素,这允许在没有标识元素的情况下使用reduce或空流的回退值。

You can use it like你可以像这样使用它

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

But the operation must be associative, like addition, multiplication, or string concatenation, etc. If you want to support non-associative operations, those operations would have to override operation(T first, T... args) with an appropriate implementation.但是操作必须是关联的,例如加法、乘法或字符串连接等。如果要支持非关联操作,这些操作必须使用适当的实现覆盖operation(T first, T... args) Overriding it doesn't work with lambda expressions.覆盖它不适用于 lambda 表达式。 The alternative would be to have an interface for arbitrary calculations and a specialization for associative operations:另一种方法是拥有一个用于任意计算的接口和一个专门用于关联操作的接口:

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

Then, non-associative operations can implement Calculator<T> via a lambda expression handling all arguments while associative operations can be implemented like:然后,非关联操作可以通过处理所有 arguments 的 lambda 表达式来实现Calculator<T> ,而关联操作可以像这样实现:

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

The necessity for a type cast could be eliminated by a factory method, using type inference for its parameter.工厂方法可以消除类型转换的必要性,使用类型推断为其参数。 However, when we are at this point, we could remember that there is already a functional interface type for operations with two arguments which the examples are even using, implicitly.然而,当我们在这一点上,我们可以记住已经有一个函数接口类型用于与两个 arguments 的操作,示例甚至隐含地使用它们。 So another alternative would be:所以另一种选择是:

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

This still allows arbitrary implementations via lambda expression while associative operations can be implemented with a simplified:这仍然允许通过 lambda 表达式进行任意实现,而关联操作可以通过简化的方式实现:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM