2017-09-16 2 views
3

J'ai eu le problème suivant: une méthode devrait prendre dans une bi-fonction qui prend 2 arguments - l'un est de type Collection<T> et l'autre est T; la fonction réelle pourrait être réellement Collection::remove ou Collection::add, ou une opération plus complexe; la fonction réelle est utilisée pour plus d'une douzaine de collection et il existe plusieurs types de valeurs et de collections dans la fonction.Est-ce possible de faire en utilisant simple BiFunction dans la définition de la méthode

Au départ, il n'y avait pas généricité - il n'y avait que Collection<String> s et les éléments étaient String s afin de déclarer l'argument que BiFunction<Collection<String>, String, Boolean> fonctionnait très bien:

List<String> idCodes; 
void visitElement(Element e, 
        BiFunction<Collection<String>, String, Boolean> elementOp) { 
    elementOp.apply(idCodes, e.getIdCode()); 
} 

Cependant j'ajouté d'autres types de collections aussi, et a constaté que Je ne pouvais plus savoir comment utiliser BiFunction génériquement:

List<String> idCodes; 
List<Integer> weights; 

void visitElement(Element e, 
        BiFunction<...> elementOp) { 
    elementOp.apply(idCodes, e.getIdCode()); 
    elementOp.apply(weights, e.getWeight()); 
} 

, mais a échoué - je pouvais obtenir des erreurs de compilation dans un endroit ou un autre, peu importe ce que j'utilise d pour les paramètres de type, il échouerait.

Un de mes tentatives était

<T> void visitElement(Element e, 
         BiFunction<Collection<T>, T, Boolean> elementOp) 

échouerait pas lors du passage à Collection::add mais lors de l'application en fait la fonction à Collection<String> et String; ou Collection<Integer> et int.

Ensuite, je fait une autre interface:

interface ElementOp { 
    <T> boolean apply(Collection<T> collection, T item); 
} 

Et pas surprenant cela fonctionne maintenant exactement comme je le voulais. Par conséquent, ma question est la suivante:

Dois-je vraiment utiliser

interface ElementOp { 
    <T> boolean apply(Collection<T> collection, T item); 
} 

void visitElement(Element e, 
        ElementOp elementOp) { 
    elementOp.apply(idCodes, e.getIdCode()); 
    elementOp.apply(weights, e.getWeight()); 
} 

ou serait-il possible d'utiliser une certaine façon BiFunction pour ce cas?

P.S. J'utilise le compilateur Eclipse 4.3 Mars Java, il se peut donc que cela ne fonctionne pas à cause d'un bug.

Répondre

2

Vous ne pouvez pas utiliser un BiFunction avec T générique qui gère les deux cas (String et Integer).

Ce code ne peut pas compiler:

<T> void visitElement(Element e, 
     BiFunction<Collection<T>, T, Boolean> elementOp) { 
    ... 
    elementOp.apply(idCodes, e.getIdCode()); 
    elementOp.apply(weights, e.getWeight()); 
} 

comme BiFunction est une classe générique et vous paramétrés avec Collection<T> et T comme arguments de la fonction.
Vous ne pouvez donc passer Collection<T> et T objets/variables pendant que vous passez Collection<String> et String dans l'appel de poing et Collection<Integer> et Integer dans le second.

Avec cette interface personnalisée, les choses sont différentes:

interface ElementOp { 
    <T> boolean apply(Collection<T> collection, T item); 
} 

Cela fonctionne:

elementOp.apply(idCodes, e.getIdCode()); 
elementOp.apply(weights, e.getWeight()); 

comme contraire à BiFunction il peut accepter comme paramètres une variable déclarée avec toute la classe.
La chose à retenir est que ElementOp n'est pas une classe générique.
T est en effet seulement une méthode générique de portée qui infère le type des types d'arguments passés.


Pour répondre à votre exigence: appeler plusieurs fois la même méthode (ou Collection.add()Collection.remove()) mais avec args de différents types (ou StringInteger), vous ne voulez pas utiliser un BiFunction<T,Collection<T>, Boolean> générique.
L'interface fonctionnelle personnalisée que vous avez introduite convient beaucoup mieux.

2

Eh bien votre première tentative ne fonctionne pas parce que quand vous avez quelque chose comme ceci:

<T> void visitElement(Element e, BiFunction<Collection<T>, T, Boolean> elementOp) { 
    // some code   
} 

Tout ce qui visitElement sait sont T types; il sait que elementOp est de type BiFunction<Collection<T>, T, Boolean>; un T qui sera déduit par le compilateur.

Je ne vois aucune raison de présenter ici un autre interface, quand vous pouvez simplement changer la méthode un peu. Notez également que je l'ai utilisé BiConsumer et non BiFunction puisque vous n'utilisez pas le résultat de toute façon:

void visitElement(T value, BiConsumer<Collection<T>, T> elementOp, Collection<T> elements) { 
     elementOp.accept(elements, value); 
} 

et de l'utiliser:

BiConsumer<Collection<String>, String> bi = Collection::remove; 

    Element e = ... 
    Collection<String> idCodes...; 
    visitElement(e.getIdCode(), bi, idCodes);