2016-03-11 10 views
6

Se ho, per esempio, un'interfaccia come questa:Java8: esiste un modo per ottenere un riferimento al metodo di istanza da un riferimento al metodo di classe?

public interface FooBar<A, B> { 
    B foo(A a); 

    B bar(A a); 
} 

C'è un modo per prendere un metodo di riferimento a livello di classe come FooBar::bar e ottenere un riferimento metodo di istanza?

ie. se ho

FooBar myFooBarInstance; 
BiFunction<FooBar<A, B>, A, B> barFunction = FooBar::bar; 

C'è un modo semplice per ottenere un'istanza Function<A,B> che corrisponde a ciò che avrei avuto se ho definito

Function<A, B> myBarFunction = myFooBarInstance::bar; 

risposta

7

Che cosa si vuole fare è chiamata “applicazione funzione parziale” nel mondo della programmazione di funzioni o semplicemente "legare un valore a un parametro" in termini meno funzionali. Non c'è alcun metodo incorporato per questo, ma è facile da scrivere il proprio:

public static <T,U,R> Function<U,R> bind(BiFunction<T,U,R> f, T t) { 
    return u -> f.apply(t, u); 
} 

Quindi è possibile utilizzare nel vostro caso:

FooBar<X,Y> instance=…; 
BiFunction<FooBar<X,Y>,X,Y> barFunction=FooBar::bar; 
Function<X,Y> myBarFunction=bind(barFunction, instance); 

o semplicemente

// equivalent to myBarFunction=instance::bar 
Function<X,Y> myBarFunction=bind(FooBar::bar, instance); 

Nota che il metodo di utilità è limitato alle interfacce funzionali che si stanno utilizzando, ad esempio Function e BiFunction, non ai riferimenti al metodo. Funziona con qualsiasi BiFunction, sia implementato come riferimento al metodo, espressione lambda o classe ordinaria. Ma sarà utile solo se hai specificamente bisogno di un'istanza Function, non di un'interfaccia funzionale arbitraria che prenda un parametro. È possibile convertire un Function in un altro tipo di funzione a singolo argomento utilizzando ::apply ma l'utilizzo di bind(bifunc, value)::apply non offre alcun vantaggio rispetto a x -> bifunc.apply(value, u) nel luogo in cui è richiesta l'istanza.

Quindi se si deve convertire BiFunction s a Function s vincolando un primo argomento molto spesso, il metodo di utilità potrebbe essere utile. Altrimenti, basta usare un'espressione lambda nel contesto in cui è presente il tipo di target effettivo. Naturalmente, è possibile scrivere un metodo simile anche per altre interfacce, ma questo sarà di nuovo utile solo se ne hai bisogno per quella specifica interfaccia spesso.

Per quanto riguarda le funzioni che richiedono più parametri, poiché l'API Java non fornisce tali interfacce funzionali, sarà necessario definire autonomamente l'interfaccia appropriata, che offre la possibilità di fornire la funzione di collegamento direttamente nell'interfaccia come metodi default, ad es.

interface MyFunc3<A,B,C,R> { 
    R apply(A a, B b, C c); 
    default BiFunction<B,C,R> bind(A a) { 
     return (b, c) -> apply(a, b, c); 
    } 
} 
interface MyFunc4<A,B,C,D,R> { 
    R apply(A a, B b, C c, D d); 
    default MyFunc3<B,C,D,R> bind(A a) { 
     return (b, c, d) -> apply(a, b, c, d); 
    } 
} 

poi, se si dispone di una funzione come

MyFunc4<U, W, X, Y, Z> func = …; 

si può fare qualcosa di simile

MyFunc3<W, X, Y, Z> f3 = func.bind(u); 

o

BiFunction<X, Y, Z> f2 = func.bind(u).bind(w); 
+1

Brillante spiegazione come al solito, Holger. Non avevo considerato semplicemente l'aggiunta di un metodo predefinito per associare un'istanza a un'interfaccia funzionale, ma è un trucco davvero utile. –

3

mi sono reso conto che la risposta è abbastanza semplice una volta ho pensato . Posso fare questo:

Function<A, B> instanceFunction = (a)->barFunction.apply(myFooBar, a); 

diventa davvero brutto se si dispone di metodi che prendono più di un paio di parametri, ma funziona ...

+0

Questo funziona, ma non è molto generico. .. Qualcuno ha una ragione modo elegante e raffinato che non dipende dalla conoscenza della firma del metodo in anticipo? So che posso costruire le cose con il riflesso, ma sarebbe bello sapere che c'era un modo più bello. –

+0

La tipizzazione statica richiede che i tipi siano noti al momento della compilazione. Non c'è modo di farlo genericamente senza generare codice byte, né generare codice e compilarlo in fase di runtime. In Java, una soluzione migliore è evitare di doverlo fare in primo luogo e scrivere codice più specifico. Un linguaggio più dinamico potrebbe gestirlo, ma hai tutti gli aspetti negativi di avere un linguaggio dinamico. –

Problemi correlati