Объявить массив lambdas в Java

Я хотел бы создать массив лямбда. Проблема в том, что лямбда может отличаться друг от друга. Пример:

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

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

Теперь, как я могу объявить список, который может содержать как I0, так и I1?

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

Очевидно, что Object не работает.

Ответ 1

Сначала вы должны назначить лямбда-выражения для переменных типов функциональных интерфейсов.

В противном случае компилятор не может вывести типы этих лямбда-выражений.

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

Тем не менее, я не уверен, какой смысл хранить эти лямбда-выражения в List<Object>. Вы не можете использовать их, не возвращая их к отдельным типам функциональных интерфейсов.

Ответ 2

Вы можете применить к соответствующему интерфейсу, например:

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

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

Он становится еще короче (не обязательно лучше):

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

Ответ 3

Из того, что я собираю, вы пытаетесь сделать что-то вроде этого:

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

Этот комментарий - довольно большая сделка; как вы его реализуете?

Вы можете проверить тип во время каждого раунда:

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

Подтверждение каждого возможного типа - это огромная боль, и если он зависит от позиции, он будет запускаться только один раз, поэтому он будет излишним.

Моя рекомендация зависит от того, будет ли это статичным (ваш код работает только на одном конкретном наборе аргументов когда-либо) или динамическом (его нужно запускать для всех видов аргументов). Для статического кода я бы просто применил lambdas без цикла:

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

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

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

Или, желательно, если вы можете упростить свои лямбды:

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

Я чувствую, что ваше решение в конечном счете менее сложно, чем любое из них. Просто попытайтесь помочь вам в правильном направлении.

Ответ 4

Вы можете заставить свои интерфейсы использовать 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
        );
    }
}