web-dev-qa-db-fra.com

Java 8 lambda Void argument

Disons que j'ai l'interface fonctionnelle suivante dans Java 8:

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

Et dans certains cas, j'ai besoin d'une action sans arguments ni type de retour. Alors j'écris quelque chose comme ça:

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

Cependant, cela me donne une erreur de compilation, je dois l’écrire en tant que

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

Ce qui est moche. Est-il possible de supprimer le paramètre de type Void?

153
Wickoo

La syntaxe que vous recherchez est possible avec une petite fonction d'assistance qui convertit un Runnable en Action<Void, Void> (vous pouvez le placer dans Action par exemple):

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

Utilisez Supplier s'il ne prend rien, mais renvoie quelque chose.

Utilisez Consumer s'il faut quelque chose, mais ne retourne rien.

Utilisez Callable s'il renvoie un résultat et risque de lancer (ressemblant le plus à Thunk en termes généraux CS).

Utilisez Runnable s'il ne fait ni l'un ni l'autre et ne peut pas lancer.

397
x1a0

Le lambda:

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

représente en réalité une implémentation pour une interface comme:

public interface Something {
    void action();
}

qui est complètement différent de celui que vous avez défini. C'est pourquoi vous obtenez une erreur.

Puisque vous ne pouvez pas étendre votre @FunctionalInterface, ni en introduire un nouveau, alors je pense que vous n’avez pas beaucoup d’options. Vous pouvez utiliser les interfaces Optional<T> pour indiquer que certaines valeurs (type de retour ou paramètre de méthode) sont manquantes. Cependant, cela ne rendra pas le corps lambda plus simple.

36
Konstantin Yovkov

Vous pouvez créer une sous-interface pour ce cas particulier:

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

Il utilise une méthode par défaut pour remplacer la méthode paramétrée héritée Void execute(Void), en déléguant l'appel à la méthode plus simple void execute().

Le résultat est que son utilisation est beaucoup plus simple:

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

Ce n'est pas possible. Une fonction ayant un type de retour non nul (même si c'est Void) doit renvoyer une valeur. Cependant, vous pouvez ajouter des méthodes statiques à Action qui vous permettent de "créer" un Action:

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

Cela vous permettrait d'écrire ce qui suit:

// 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

Ajouter une méthode statique dans votre interface fonctionnelle

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

}

sortie

Do nothing!
4
MCHAppy

Juste pour référence, quelle interface fonctionnelle peut être utilisée pour la référence de méthode dans les cas où la méthode lève et/ou renvoie une valeur.

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

Je ne pense pas que ce soit possible, car les définitions de fonction ne correspondent pas dans votre exemple.

Votre expression lambda est évaluée exactement comme

void action() { }

alors que votre déclaration ressemble à

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

à titre d'exemple, si vous avez l'interface suivante

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

le seul type de fonction (lors de l’instanciation) qui sera compatible ressemble à

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

et soit le manque d’instruction de retour, soit l’argument vous donnera une erreur de compilation.

Par conséquent, si vous déclarez une fonction qui prend un argument et en retour un, je pense qu’il est impossible de la convertir en une fonction qui n’est ni l'une ni l'autre des solutions mentionnées ci-dessus.

3
pnadczuk