Может ли Java-лямбда иметь более 1 параметра?

158

В Java возможно ли лямбда принимать несколько разных типов?

Т.е. работает одна переменная:

    Function <Integer, Integer> adder = i -> i + 1;
    System.out.println (adder.apply (10));

Varargs также работают:

    Function <Integer [], Integer> multiAdder = ints -> {
        int sum = 0;
        for (Integer i : ints) {
            sum += i;
        }
        return sum;
    };

    //.... 
    System.out.println ((multiAdder.apply (new Integer [] { 1, 2, 3, 4 })));

Но я хочу что-то, что может принимать много разных типов аргументов, например:

    Function <String, Integer, Double, Person, String> myLambda = a , b, c, d->  {
    [DO STUFF]
    return "done stuff"
    };

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

Я осмотрел Google и проверил функциональный пакет Java, но не смог найти. Это возможно?

Лев Уфимцев
источник

Ответы:

178

Это возможно, если вы определите такой функциональный интерфейс с несколькими типами параметров. Нет такого встроенного типа. (Есть несколько ограниченных типов с несколькими параметрами.)

@FunctionalInterface
interface Function6<One, Two, Three, Four, Five, Six> {
    public Six apply(One one, Two two, Three three, Four four, Five five);
}

public static void main(String[] args) throws Exception {
    Function6<String, Integer, Double, Void, List<Float>, Character> func = (a, b, c, d, e) -> 'z';
}

Я назвал это Function6здесь. Имя на ваше усмотрение, просто постарайтесь не конфликтовать с существующими именами в библиотеках Java.


Также нет способа определить переменное число параметров типа, если это то, о чем вы спрашивали.


Некоторые языки, такие как Scala, определяют количество встроенных таких типов с параметрами типов 1, 2, 3, 4, 5, 6 и т. Д.

Сотириос Делиманолис
источник
58
Вы всегда можете использовать карри:Function<One, Function<Two, Function<Three, Function<Four, Function<Five, Six>>>>> func = a -> b -> c -> d -> e -> 'z';
Хольгер
@SotiriosDelimanolis: Я бы предпочел выбрать другое имя, Functionстолкновение с java.util.function.Function<T,R>которым может быть непонятным для начинающих.
Николай
@Nikolas Это разумно. Ред.
Сотириос Делиманолис
39

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

@FunctionalInterface
public interface FourParameterFunction<T, U, V, W, R> {
    public R apply(T t, U u, V v, W w);
}

Если есть более одного параметра, вам нужно поместить скобки вокруг списка аргументов, например так:

FourParameterFunction<String, Integer, Double, Person, String> myLambda = (a, b, c, d) -> {
    // do something
    return "done something";
};
tbodt
источник
35

Для этого случая вы можете использовать интерфейсы из библиотеки по умолчанию (Java 1.8):

java.util.function.BiConsumer
java.util.function.BiFunction

Существует небольшой (не лучший) пример метода по умолчанию в интерфейсе:

default BiFunction<File, String, String> getFolderFileReader() {
    return (directory, fileName) -> {
        try {
            return FileUtils.readFile(directory, fileName);
        } catch (IOException e) {
            LOG.error("Unable to read file {} in {}.", fileName, directory.getAbsolutePath(), e);
        }
        return "";
    };
}}
ayurchuk
источник
5
Вы получите больше голосов от поклонников Java8, если вы измените свой вопрос, чтобы проиллюстрировать, как эти интерфейсы могут быть использованы для удовлетворения требований.
Мартин Коуи,
3
BiFunction позволяет определять только функции с двумя аргументами, вопрос о функциях с любым количеством аргументов
Дмитрий Клочков
8

Чтобы использовать лямбду: Существует три типа операций:
1. Принять параметр -> Потребитель
2. Тестовый параметр вернуть логическое значение -> Предикат
3. Управление параметром и возвращаемое значение -> Функция

Функциональный интерфейс Java с двумя параметрами:
однопараметрический интерфейс Функция предиката
потребителя Двухпараметрический интерфейс BiConsumer BiPredicate BiFunction







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

@FunctionalInterface
public interface FiveParameterConsumer<T, U, V, W, X> {
    public void accept(T t, U u, V v, W w, X x);
}
amoljdv06
источник
1

Другая альтернатива, не уверенная, применима ли она к вашей конкретной проблеме, но к некоторым она может быть применима, это использовать UnaryOperatorв библиотеке java.util.function. где он возвращает тот же тип, который вы укажете, поэтому вы помещаете все свои переменные в один класс и это как параметр:

public class FunctionsLibraryUse {

    public static void main(String[] args){
        UnaryOperator<People> personsBirthday = (p) ->{
            System.out.println("it's " + p.getName() + " birthday!");
            p.setAge(p.getAge() + 1);
            return p;
        };
        People mel = new People();
        mel.setName("mel");
        mel.setAge(27);
        mel = personsBirthday.apply(mel);
        System.out.println("he is now : " + mel.getAge());

    }
}
class People{
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

Таким образом, класс, который у вас есть, в этом случае Personможет иметь множество переменных экземпляра и не должен будет изменять параметр вашего лямбда-выражения.

Для тех, кто заинтересован, я написал заметки о том, как использовать библиотеку java.util.function: http://sysdotoutdotprint.com/index.php/2017/04/28/java-util-function-library/

mel3kings
источник
1

Вы также можете использовать библиотеку jOOL - https://github.com/jOOQ/jOOL

Он уже подготовил функциональные интерфейсы с различным количеством параметров. Например, вы можете использовать org.jooq.lambda.function.Function3и т. Д. От Function0до Function16.

PetroCliff
источник