2017-08-18 6 views
11

Compte tenu des fonctions de méthode Java 8:Façon concise de composer des références de méthode Java?

class Foo { Bar getBar() {} } 
class Bar { Baz getBaz() {} } 

Composition des deux accesseurs ressemble:

Function<Foo, Bar> getBarFromFoo = Foo::getBar; 
Function<Bar, Baz> getBazFromBar = Bar::getBaz; 
Function<Foo, Baz> getBazFromFoo = getBarFromFoo.andThen(getBazFromBar); 

Y at-il une façon plus concise? Cela semble fonctionner

((Function<Foo, Bar>) Foo::getBar).andThen(Bar::getBaz) 

Mais c'est plutôt moche. Les parens externes ont un sens pour des raisons de préséance, mais pourquoi la distribution est-elle nécessaire?

(Foo::getBar::getBaz serait bien, mais hélas ...)

+0

Pourquoi ne pas faire 'foo -> foo.getBar() :: getBaz' ? On dirait que vous êtes trop compliqué. Y a-t-il quelque chose qui me manque? –

+2

@VinceEmigh, voulez-vous dire 'foo -> foo.getBar(). GetBaz()'? Sinon, cela n'a aucun sens – Andrew

+0

@VinceEmigh, la façon dont vous avez suggéré n'est pas assez flexible. Il est préférable de préparer des mappeurs simples ('a-> b',' b-> c') en les composant avec des opérations simples comme 'compose',' andThen' au moment de l'exécution plutôt que de construire tous les cas possibles au moment de la compilation. – Andrew

Répondre

3

Il n'y a aucun moyen dédié de la composition des fonctions en Java autre que andThen().

Vous devez effectuer la conversion car Foo::getBar est ambigu. ** Il pourrait correspondre à toutes les interfaces ayant une signature de méthode similaire.

Malheureusement, ((Function<Foo, Bar>) Foo::getBar).andThen(Bar::getBaz) est le meilleur que vous pouvez faire.

10

Définissons une interface fonctionnelle:

@FunctionalInterface 
interface MyFunctionalInterface { 
    Bar getBar(Foo f); 
} 

Nous pouvons simplifier la référence de la méthode Foo::getBar un peu,

(Foo foo) -> foo.getBar(); 

qui signifie "prendre un Foo et retourner un Bar". Pour cette description, un grand nombre de méthodes sont appropriées (par exemple, notre interface avec le getBar et un Funtion<Foo, Bar> avec son apply):

MyFunctionalInterface f1 = (Foo foo) -> foo.getBar(); 
Function<Foo, Bar> f2 = (Foo foo) -> foo.getBar(); 

C'est la réponse à la question de savoir pourquoi la distribution est nécessaire.


Pour répondre à la question de savoir s'il y a une façon plus concise affirmativement, nous devons définir un contexte. Le contexte nous donne clairement un Function de continuer à travailler avec:

class Functions { 
    public static <I, O> Function<I, O> of(Function<I, O> function) { 
     return function; 
    } 
} 

Functions.of(Foo::getBar).andThen(Bar::getBaz); 
1

Peut-être juste utiliser une expression lambda?

x -> x.getBar().getBaz() 

Il n'existe pas d'autre moyen de composer des fonctions autres que celles que vous avez déjà suggérées en raison de l'ambiguïté de type. Ce n'est même pas beaucoup plus long que Foo::getBar::getBaz

0

C'est le meilleur que vous obtiendrez. Si vous pensez que cela fonctionnerait:

Foo::getBar::getBaz 

ce ne sera pas.C'est parce que Foo::getBar est une poly expression - cela dépend du contexte utilisé - il pourrait être un Function, mais pourrait également être un Predicate par exemple; donc ça pourrait potentiellement s'appliquer à beaucoup de choses, donc le casting est juste nécessaire là-bas.

Vous pourriez cacher cela derrière une méthode qui ferait le chaînage et andThen, mais le problème est toujours là.

EDIT

Voir un exemple ici:

public static void cool(Predicate<Integer> predicate) { 

} 

public static void cool(Function<Integer, String> function) { 

} 

et l'expression cool(i -> "Test"); échouera à compiler

+0

Merci. Je sais que ça ne marchera pas. Juste souhaitant ... L'inferencer de type devrait savoir que ce n'est pas un prédicat car il ne renvoie pas de booléen. Je ne suis pas un assistant d'inférence de type Java, mais il semble logique que puisque Foo :: getBar satisfasse toute interface fonctionnelle de la forme Foo-> Bar et que getBaz satisfasse Bar-> Baz, alors il semble raisonnable que 'Foo :: getBar: : getBaz' est une expression poly qui satisfait toute interface fonctionnelle de la forme Foo-> Baz. – Gene

+0

@Gene le 'Predicate' et' Function' étaient juste un exemple pour prouver qu'il s'agit bien d'expressions poly (voir edit) ... Et 'type inference' pour lambdas et les références de méthodes ne sont pas si évidentes; c'est la raison pour laquelle probablement ce chaînage n'a pas été implémenté du tout - c'est * de loin * pas trivial – Eugene

+0

J'ai en fait écrit un inferencer de type. Je sais que ce n'est pas trivial. Cela ne veut pas dire que c'est impossible. – Gene