Подтвердить что ты не робот

Java 8: Лямбда с переменными аргументами

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

Я хочу сделать что-то вроде:

test((arg0, arg1) -> me.call(arg0, arg1));
test((arg0, arg1, arg2) -> me.call(arg0, arg1, arg2));
...

Есть ли способ сделать это элегантно, не определяя 10 интерфейсов, по одному для каждого аргумента?

Обновление

Я использую несколько интерфейсов, простирающихся от интерфейса не-метода, и я перегружаю этот метод.

Пример для двух аргументов:

interface Invoker {}
interface Invoker2 extends Invoker { void invoke(Object arg0, Object arg1);}
void test(Invoker2 invoker, Object ... arguments) {
    test((Invoker)invoker, Object ... arguments);
}

void test(Invoker invoker, Object ... arguments) {
    //Use Reflection or whatever to access the provided invoker
}

Я надеюсь на возможность заменить 10 интерфейсов invoker и 10 перегруженных методов одним решением.

У меня есть разумный случай использования и, пожалуйста, не задавайте таких вопросов, как "Зачем вам это делать?" и "В чем проблема, которую вы пытаетесь решить?" или что-то в этом роде. Просто знайте, что я подумал об этом, и это законная проблема, которую я пытаюсь решить.

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

В принципе, как указано выше, подумайте о методе, который работает с другим числом атрибутов в lambda.

4b9b3361

Ответ 1

Последнее решение, которое я использую в настоящее время, - это определение иерархии интерфейсов (как указано в вопросе) и использование методов по умолчанию, чтобы избежать сбоя. Псевдо код выглядит следующим образом:

interface VarArgsRunnable {
     default void run(Object ... arguments) {
          throw new UnsupportedOperationException("not possible");
     }
     default int getNumberOfArguments() {
          throw new UnsupportedOperationException("unknown");
     }
}

и интерфейс для четырех аргументов, например:

@FunctionalInterface
interface VarArgsRunnable4 extends VarArgsRunnable {
     @Override
     default void run(Object ... arguments) {
          assert(arguments.length == 4);
          run(arguments[0], arguments[1], arguments[2], arguments[3]);
     }

     void run(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4);

     @Override
     default int getNumberOfArguments() {
          return 4;
     }
}

Определив 11 интерфейсов от VarArgsRunnable0 до VarArgsRunnable10, перегрузка метода становится довольно простой.

public void myMethod(VarArgsRunnable runnable, Object ... arguments) {
     runnable.run(arguments);
}

Так как Java не может составить Лямбду, найдя правильный расширенный функциональный интерфейс VarArgsRunnable, используя что-то вроде instance.myMethod((index, value) -> doSomething(to(index), to(value)), 10, "value"), нужно перегрузить метод, используя правильный интерфейс.

public void myMethod(VarArgsRunnable2 runnable, Object arg0, Object arg1) {
    myMethod((VarArgsRunnable)runnable, combine(arg0, arg1));
}

private static Object [] combine(Object ... values) {
    return values;
}

Так как для этого требуется, чтобы объект Object был привязан к любому присвоенному типу, используя to(...), можно использовать параметризацию с помощью Generics, чтобы избежать этого использования.

Метод to выглядит следующим образом:   public static T to (значение объекта) {       return (T);//Подавить это предупреждение   }

Пример является хромым, но я использую его для вызова метода с несколькими аргументами, являющегося перестановкой всех потенциальных комбинаций (для целей тестирования), таких как:

run((index, value) -> doTheTestSequence(index, value), values(10, 11, 12), values("A", "B", "C"));

Итак, эта маленькая строка запускает 6 вызовов. Таким образом, вы видите, что это аккуратный помощник, способный тестировать несколько элементов в одной строке, вместо того, чтобы определять намного больше или использовать несколько методов в TestNG и любом...

PS: Не нужно использовать отражения, это очень хорошо, так как он не может потерпеть неудачу и, скорее всего, сохранит аргумент count wise.

Ответ 2

В Java вам нужно использовать такой массив.

test((Object[] args) -> me.call(args));

Если call принимает переменную массива args, это будет работать. Если нет, вы можете использовать отражение, чтобы сделать вызов.

Ответ 3

Я считаю, что следующий код должен быть адаптирован к тому, что вы хотите:

public class Main {
    interface Invoker {
      void invoke(Object ... args);
    }

    public static void main(String[] strs) {
        Invoker printer = new Invoker() {
            public void invoke(Object ... args){
                for (Object arg: args) {
                    System.out.println(arg);
                }
            }
        };

        printer.invoke("I", "am", "printing");
        invokeInvoker(printer, "Also", "printing");
        applyWithStillAndPrinting(printer);
        applyWithStillAndPrinting((Object ... args) -> System.out.println("Not done"));
        applyWithStillAndPrinting(printer::invoke);
    }

    public static void invokeInvoker(Invoker invoker, Object ... args) {
        invoker.invoke(args);
    }

    public static void applyWithStillAndPrinting(Invoker invoker) {
        invoker.invoke("Still", "Printing"); 
    }
}

Обратите внимание, что вам не нужно создавать и передавать в lambda мне .call, потому что у вас уже есть ссылка на этот метод. Вы можете вызвать test(me::call) так же, как я вызываю applyWithStillAndPrinting(printer::invoke).