2015-04-29 10 views
85

Diciamo Ho il seguente interfaccia funzionale in Java 8:Java 8 lambda argomento Void

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

E per alcuni casi ho bisogno un'azione senza argomenti o il tipo di ritorno. Così scrivo qualcosa di simile:

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

Tuttavia, mi dà errore di compilazione, ho bisogno di scrivere come

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

Il che è brutto. C'è un modo per sbarazzarsi del parametro di tipo Void?

+1

Date un'occhiata a http://stackoverflow.com/questions/14319787/how-to-specify-function-types-for-void-not-void-methods-in-java8 – BobTheBuilder

+4

Se avete bisogno di un'azione, come l'hai definito, non è possibile. Tuttavia, il tuo primo esempio potrebbe rientrare in un 'Runnable', che cosa stai cercando' Runnable r =() -> System.out.println ("Do nothing!"); ' –

+1

@BobTheBuilder Non voglio utilizzare un consumatore come suggerito in quel post. – Wickoo

risposta

76

La sintassi che cercate è possibile con una funzione di supporto poco che converte un Runnable in Action<Void, Void> (è possibile inserirlo in Action per esempio):

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")); 
+4

Questa è la soluzione più pulita che si possa ottenere, IMO, quindi +1 (o con un metodo statico nell'interfaccia stessa) –

+0

La soluzione di Konstantin Yovkov di seguito (con @FunctionalInterface) è una soluzione migliore, perché non coinvolge generici e non richiede codice extra. – uthomas

+0

@uthomas Spiacente, non vedo una risposta che coinvolga '@ FunctionalInterface'. Dice semplicemente che non è possibile estenderlo ... – Matt

30

Il lambda:

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

in realtà rappresenta un'implementazione per un'interfaccia simile:

public interface Something { 
    void action(); 
} 

che è completamente diverso da quello che hai definito. Ecco perché ricevi un errore.

Dal momento che non è possibile estendere il numero @FunctionalInterface, né introdurre uno nuovo di zecca, quindi penso che non ci siano molte opzioni. È possibile utilizzare le interfacce Optional<T> per indicare che alcuni valori (tipo restituito o parametro metodo) mancano, tuttavia. Tuttavia, questo non renderà il corpo lambda più semplice.

+0

Il problema è che la funzione 'Something' non può essere un sottotipo del mio tipo' Action', e non posso avere due tipi diversi. – Wickoo

+0

Acquista il mio aggiornamento. –

+0

Tecnicamente può, ma ha detto che vuole evitarlo. :) –

3

Non credo sia possibile, perché le definizioni di funzione non corrispondono nel tuo esempio.

tua espressione lambda viene valutata esattamente come

void action() { } 

mentre il vostro dichiarazione sembra

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

come un esempio, se hai seguente interfaccia

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

l'unico tipo di la funzione (durante l'istanziazione) che sarà compatibile è simile a

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

e l'assenza dell'istruzione return o dell'argomento causerà un errore del compilatore.

Pertanto, se si dichiara una funzione che accetta un argomento e ne restituisce uno, penso che sia impossibile convertirlo in funzione che non fa nessuno dei due sopra menzionati.

5

Questo non è possibile. Una funzione con un tipo di reso non vuoto (anche se è Void) deve restituire un valore.Tuttavia è possibile aggiungere metodi statici per Action che permette di "creare" 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; 
    } 
} 

che permetterebbe di scrivere il seguente:

// 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)); 
22

È possibile creare un sub-interfaccia per quel caso speciale:

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

Esso utilizza una default method per l'override del metodo parametrizzato ereditato Void execute(Void), delegando ° Chiamare al metodo più semplice void execute().

Il risultato è che è molto più semplice da usare:

Command c =() -> System.out.println("Do nothing!"); 
3

Aggiungi un metodo statico all'interno della vostra interfaccia funzionale

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

} 

uscita

Do nothing! 
+2

Cosa c'è di diverso dalle risposte già pubblicate? –

2

Solo per riferimento, che l'interfaccia funzionale può essere utilizzata per il metodo riferimento nel caso in cui il metodo genera e/o restituisce un valore.

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

Utilizzare Supplier se non serve nulla.

Utilizzare Consumer se non restituisce nulla.

Utilizzare Runnable se non ne ha.

+0

Come esempio ho fatto questo per racchiudere la chiamata di ritorno "void": 'public static void wrapCall (Runnable r) {r.run(); } '. Grazie – Maxence

+0

bella risposta. Breve e preciso. –