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
?
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"));
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.
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.
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!");
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));
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!
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
}
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.