Kann ein Java Lambda mehr als einen Parameter haben?

156

Ist es in Java möglich, dass ein Lambda mehrere verschiedene Typen akzeptiert?

Dh: Einzelne Variable funktioniert:

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

Varargs funktionieren auch:

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

Aber ich möchte etwas, das viele verschiedene Arten von Argumenten akzeptieren kann, z.

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

Die Hauptanwendung besteht darin, der Einfachheit halber kleine Inline-Funktionen in Funktionen zu haben.

Ich habe mich bei Google umgesehen und das Funktionspaket von Java überprüft, konnte es aber nicht finden. Ist das möglich?

Leo Ufimtsev
quelle

Antworten:

178

Es ist möglich, wenn Sie eine solche Funktionsschnittstelle mit mehreren Typparametern definieren. Es gibt keinen solchen eingebauten Typ. (Es gibt einige begrenzte Typen mit mehreren Parametern.)

@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';
}

Ich habe es Function6hier genannt. Der Name liegt in Ihrem Ermessen. Versuchen Sie nur, nicht mit vorhandenen Namen in den Java-Bibliotheken in Konflikt zu geraten.


Es gibt auch keine Möglichkeit, eine variable Anzahl von Typparametern zu definieren, wenn Sie danach gefragt haben.


Einige Sprachen, wie Scala, definieren eine Reihe solcher eingebauter Typen mit 1, 2, 3, 4, 5, 6 usw. Typparametern.

Sotirios Delimanolis
quelle
58
Sie können immer Currying verwenden:Function<One, Function<Two, Function<Three, Function<Four, Function<Five, Six>>>>> func = a -> b -> c -> d -> e -> 'z';
Holger
@SotiriosDelimanolis: Ich würde lieber einen anderen Namen wählen, von Functiondem Zusammenstöße java.util.function.Function<T,R>für die Anfänger unklar sein könnten.
Nikolas
@ Nikolas Das ist vernünftig. Bearbeitet.
Sotirios Delimanolis
39

Für etwas mit 2 Parametern könnten Sie verwenden BiFunction. Wenn Sie mehr benötigen, können Sie Ihre eigene Funktionsoberfläche wie folgt definieren:

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

Wenn es mehr als einen Parameter gibt, müssen Sie die Argumentliste in Klammern setzen, wie folgt:

FourParameterFunction<String, Integer, Double, Person, String> myLambda = (a, b, c, d) -> {
    // do something
    return "done something";
};
tbodt
quelle
34

In diesem Fall können Sie Schnittstellen aus der Standardbibliothek (Java 1.8) verwenden:

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

Es gibt ein kleines (nicht das beste) Beispiel für eine Standardmethode in der Schnittstelle:

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
quelle
4
Sie erhalten mehr Up-Votes von Java8-Fans, wenn Sie Ihre Frage ändern, um zu veranschaulichen, wie diese Schnittstellen verwendet werden können, um die Anforderung zu erfüllen.
Martin Cowie
3
Mit BiFunction können Sie nur Funktionen mit zwei Argumenten definieren. Die Frage
betrifft
8

So verwenden Sie Lambda: Es gibt drei Arten von Operationen:
1. Parameter akzeptieren -> Consumer
2. Rückgabewert boolesch testen -> Prädikat
3. Parameter und Rückgabewert manipulieren -> Funktion

Java-Funktionsschnittstelle mit bis zu zwei Parametern:
Einzelparameter-Schnittstelle
Consumer
Predicate
Function

Zwei-Parameter-Schnittstelle
BiConsumer
BiPredicate
BiFunction

Für mehr als zwei müssen Sie die Funktionsschnittstelle wie folgt erstellen (Verbrauchertyp):

@FunctionalInterface
public interface FiveParameterConsumer<T, U, V, W, X> {
    public void accept(T t, U u, V v, W w, X x);
}
amoljdv06
quelle
1

Eine andere Alternative, die nicht sicher ist, ob dies für Ihr spezielles Problem gilt, aber für einige möglicherweise zutreffend ist, ist die Verwendung UnaryOperatorin der Bibliothek java.util.function. Wenn derselbe Typ zurückgegeben wird, den Sie angegeben haben, werden alle Variablen in einer Klasse zusammengefasst und als Parameter angegeben:

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

Die Klasse, die Sie in diesem Fall haben Person, kann also zahlreiche Instanzvariablen haben und muss den Parameter Ihres Lambda-Ausdrucks nicht ändern.

Für Interessenten habe ich Hinweise zur Verwendung der Bibliothek java.util.function geschrieben: http://sysdotoutdotprint.com/index.php/2017/04/28/java-util-function-library/

mel3kings
quelle
1

Sie können auch die jOOL-Bibliothek verwenden - https://github.com/jOOQ/jOOL

Es wurden bereits Funktionsschnittstellen mit unterschiedlicher Anzahl von Parametern vorbereitet. Zum Beispiel könnten Sie org.jooq.lambda.function.Function3usw. von Function0bis zu verwenden Function16.

PetroCliff
quelle