Declare array of lambdas in Java

2020-08-11 10:01发布

问题:

I'd like to create an array of lambda. The problem is that the lambda could be different from each other. Example:

private interface I0 {
    int interface0(int a, int b);
}

private interface I1 {
    int interface1(double d);
}

Now, how can I declare a list which can contain both I0 and I1?

List<Object> test = Arrays.asList(
        (int a, int b) -> a + b,
        (double d) -> d * 2
);

Obviously Object does not work.

回答1:

You must assign the lambda expressions to variables of the functional interface types first.

Otherwise the compiler cannot infer the types of these lambda expressions.

I0 i0 = (int a, int b) -> a + b;
I1 i1 = (double d) -> (int) (d * 2);
List<Object> test = Arrays.asList(
    i0,
    i1
);

That said, I'm not sure what's the point of storing these lambda expressions in a List<Object>. You can't use them without casting them back to the individual functional interface types.



回答2:

You could cast to the respective Interface like:

List<Object> test = Arrays.asList(
    (I0) (int a, int b) -> a + b,
    (I1) (double d) -> (int) (d * 2)
);

despite this being shorter, I would also consider Eran's answer maybe it is more readable and easier to understand (if having more functions). And I also can't see the use case for such a construct...

It gets even shorter (not necessarily better):

List<Object> test = Arrays.asList(
    (I0) (a, b) -> a + b,
    (I1) d -> (int) (d * 2)
);


回答3:

From what I gather, you're trying to do something like this:

public static void main(String... args){
    List<Object> lambdas = Arrays.asList(
            (IntBinaryOperator) (int a, int b) -> a + b,
            (DoubleToIntFunction) (double d) -> (int)(d * 2)
    );
    for(int i=0;i<args.length;i++){
        // Apply lambdas.get(i) to args[i]
    }
}

That comment is a pretty big deal, though; how would you implement it?

You could check the type during each round:

    for(int i=0;i<args.length;i++){
        if(lambdas.get(i) instanceof IntBinaryOperator){
            processedArgs[i] =
                    ((IntBinaryOperator) lambdas.get(i)).applyAsInt(MAGIC_NUMBER, Integer.parseInt(args[i]));
        }else{
            // All your other cases (may take a while)
        }
    }

Validating each possible type is a huge pain, and if it's position-dependent, it's only going to run once anyway, so it's overkill.

My recommendation depends on if this is going to be static (your code runs only on one specific set of arguments ever) or dynamic (it needs to run on all kinds of arguments). For static code, I'd just apply the lambdas without a loop:

public static void main(String... args){
    processedArgs = new int[args.length];
    IntBinaryOperator op1 = (int a, int b) -> a + b;
    DoubleToIntFunction op2 =  (double d) -> (int)(d * 2);

    processedArgs[0] = op1.applyAsInt(MAGIC_NUMBER, Integer.parseInt(args[0]));
    processedArgs[1] = op2.applyAsInt(Double.parseDouble(args[1]));
}

For a dynamic solution, I'd recommend switching to a single functional interface. I would go with the maximum requirement and fill in dummy values where not needed:

public static void main(String... args){
    processedArgs = new int[args.length];
    List<DoubleBinaryOperator> ops = Arrays.asList(
            (a, b) -> a + b,
            (d, ignore) -> (d * 2)
    );
    for(int i=0;i<args.length;i++){
        processedArgs[i] = (int)ops.get(i).applyAsDouble(Double.parseDouble(args[i]), MAGIC_NUMBER);
    }
}

Or, preferably, if you can simplify your lambdas:

public static void main(String... args){
    processedArgs = new int[args.length];
    List<DoubleToIntFunction> ops = Arrays.asList(
            (d) -> (int)(d + MAGIC_NUMBER),
            (d) -> (int)(d * 2)
    );
    for(int i=0;i<args.length;i++){
        processedArgs[i] = ops.get(i).applyAsInt(Double.parseDouble(args[i]));
    }
}

I feel like your solution ultimately is less complicated than any of these. Just trying to help point you in the right direction.



回答4:

You could make your interfaces use vararg typing:

public class NaryFunctionTest {

    interface NaryFunction<R extends Number> {
        R run(R... args);
    }

    @Test
    public void test() {
        NaryFunction<Integer> f1 = (Integer[] o) -> o[0] + o[1];
        NaryFunction<Double> f2 = (Double[] o) -> o[0] *2;
        List<NaryFunction<?>> test = Arrays.asList(
            f1,
            f2
        );
    }
}


标签: java lambda