it-swarm.com.de

Java 8 lambda Void argument

Angenommen, ich habe die folgende Funktionsschnittstelle in Java 8:

interface Action<T, U> {
   U execute(T t);
}

In einigen Fällen benötige ich eine Aktion ohne Argumente oder Rückgabetyp. Also schreibe ich so etwas:

Action<Void, Void> a = () -> { System.out.println("Do nothing!"); };

Allerdings gibt es mir Kompilierungsfehler, ich muss es so schreiben

Action<Void, Void> a = (Void v) -> { System.out.println("Do nothing!"); return null;};

Welches ist hässlich. Gibt es eine Möglichkeit, den Typparameter Void zu entfernen?

153
Wickoo

Die von Ihnen gewünschte Syntax ist mit einer kleinen Hilfsfunktion möglich, die ein Runnable in Action<Void, Void> Konvertiert (Sie können es beispielsweise in Action einfügen):

public static Action<Void, Void> action(Runnable runnable) {
    return (v) -> {
        runnable.run();
        return null;
    };
}

// Somewhere else in your code
 Action<Void, Void> action = action(() -> System.out.println("foo"));
94
Matt

Verwenden Sie Supplier , wenn es nichts braucht, aber etwas zurückgibt.

Verwenden Sie Consumer , wenn es etwas braucht, aber nichts zurückgibt.

Verwenden Sie Callable , wenn es ein Ergebnis zurückgibt und werfen könnte (am ähnlichsten Thunk in allgemeinen CS-Begriffen).

Verwenden Sie Runnable , wenn es keines von beiden tut und nicht werfen kann.

397
x1a0

Der Lambda:

() -> { System.out.println("Do nothing!"); };

stellt tatsächlich eine Implementierung für eine Schnittstelle wie:

public interface Something {
    void action();
}

das ist völlig anders als das, das Sie definiert haben. Deshalb bekommst du eine Fehlermeldung.

Da Sie weder Ihren @FunctionalInterface Verlängern noch einen brandneuen Code einführen können, haben Sie meines Erachtens nicht viele Optionen. Sie können die Optional<T> - Schnittstellen verwenden, um anzuzeigen, dass einige der Werte (Rückgabetyp oder Methodenparameter) fehlen. Dies wird den Lambda-Körper jedoch nicht einfacher machen.

36

Sie können eine Unterschnittstelle für diesen speziellen Fall erstellen:

interface Command extends Action<Void, Void> {
  default Void execute(Void v) {
    execute();
    return null;
  }
  void execute();
}

Es verwendet eine Standardmethode , um die vererbte parametrisierte Methode Void execute(Void) zu überschreiben und den Aufruf an die einfachere Methode void execute() zu delegieren.

Das Ergebnis ist, dass es viel einfacher zu bedienen ist:

Command c = () -> System.out.println("Do nothing!");
28
Jordão

Das ist nicht möglich. Eine Funktion mit nicht ungültigem Rückgabetyp (auch wenn es sich um Void handelt) muss einen Wert zurückgeben. Sie können jedoch statische Methoden zu Action hinzufügen, mit denen Sie Action "erstellen" können:

interface Action<T, U> {
   U execute(T t);

   public static Action<Void, Void> create(Runnable r) {
       return (t) -> {r.run(); return null;};
   }

   public static <T, U> Action<T, U> create(Action<T, U> action) {
       return action;
   } 
}

Dann können Sie Folgendes schreiben:

// create action from Runnable
Action.create(()-> System.out.println("Hello World")).execute(null);
// create normal action
System.out.println(Action.create((Integer i) -> "number: " + i).execute(100));
5
fabian

Fügen Sie eine statische Methode in Ihre Funktionsschnittstelle ein

package example;

interface Action<T, U> {
       U execute(T t);
       static  Action<Void,Void> invoke(Runnable runnable){
           return (v) -> {
               runnable.run();
                return null;
            };         
       }
    }

public class Lambda {


    public static void main(String[] args) {

        Action<Void, Void> a = Action.invoke(() -> System.out.println("Do nothing!"));
        Void t = null;
        a.execute(t);
    }

}

Ausgabe

Do nothing!
4
MCHAppy

Nur als Referenz, welche Funktionsschnittstelle für die Methodenreferenz verwendet werden kann, wenn die Methode einen Wert auslöst und/oder zurückgibt.

void notReturnsNotThrows() {};
void notReturnsThrows() throws Exception {}
String returnsNotThrows() { return ""; }
String returnsThrows() throws Exception { return ""; }

{
    Runnable r1 = this::notReturnsNotThrows; //ok
    Runnable r2 = this::notReturnsThrows; //error
    Runnable r3 = this::returnsNotThrows; //ok
    Runnable r4 = this::returnsThrows; //error

    Callable c1 = this::notReturnsNotThrows; //error
    Callable c2 = this::notReturnsThrows; //error
    Callable c3 = this::returnsNotThrows; //ok
    Callable c4 = this::returnsThrows; //ok

}


interface VoidCallableExtendsCallable extends Callable<Void> {
    @Override
    Void call() throws Exception;
}

interface VoidCallable {
    void call() throws Exception;
}

{
    VoidCallableExtendsCallable vcec1 = this::notReturnsNotThrows; //error
    VoidCallableExtendsCallable vcec2 = this::notReturnsThrows; //error
    VoidCallableExtendsCallable vcec3 = this::returnsNotThrows; //error
    VoidCallableExtendsCallable vcec4 = this::returnsThrows; //error

    VoidCallable vc1 = this::notReturnsNotThrows; //ok
    VoidCallable vc2 = this::notReturnsThrows; //ok
    VoidCallable vc3 = this::returnsNotThrows; //ok
    VoidCallable vc4 = this::returnsThrows; //ok
}
3
SlavaL

Ich halte das nicht für möglich, da die Funktionsdefinitionen in Ihrem Beispiel nicht übereinstimmen.

Ihr Lambda-Ausdruck wird genau als ausgewertet

void action() { }

während Ihre Erklärung aussieht

Void action(Void v) {
    //must return Void type.
}

als Beispiel, wenn Sie folgende Schnittstelle haben

public interface VoidInterface {
    public Void action(Void v);
}

die einzige Art von Funktion (beim Instanziieren), die kompatibel ist, sieht aus wie

new VoidInterface() {
    public Void action(Void v) {
        //do something
        return v;
    }
}

wenn Sie keine return-Anweisung oder kein Argument angeben, erhalten Sie einen Compilerfehler.

Wenn Sie also eine Funktion deklarieren, die ein Argument annimmt und eines zurückgibt, ist es meines Erachtens unmöglich, sie in eine Funktion umzuwandeln, die keine der oben genannten Funktionen ausführt.

3
pnadczuk