Implémentation de plusieurs interfaces génériques en java


10

J'ai besoin d'une interface qui m'assure qu'une certaine méthode, y compris une signature spécifique, est disponible. Jusqu'à présent, c'est ce que j'ai:

public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

Le problème se pose lorsqu'une classe doit être mappable à plusieurs autres entités. Le cas idéal serait celui-ci (pas java):

public class Something implements Mappable<A>, Mappable<B> {
    public A mapTo(A someObject) {...}
    public B mapTo(B someOtherObject) {...}
}

Quelle serait la meilleure façon de parvenir à ce que cela reste aussi «générique» que possible?

Réponses:


10

Bien sûr, ce n'est pas quelque chose que vous pouvez faire en raison de l' effacement de type . Au moment de l'exécution, vous disposez de deux méthodes public Object mapTo(Object), qui ne peuvent évidemment pas coexister.

Malheureusement, ce que vous essayez de faire est simplement au-delà du système de type Java.

En supposant que votre type générique est toujours un type de première classe, et non lui-même générique, vous pouvez obtenir un comportement extérieur similaire en ayant la méthode mapTo(Object, Class), qui vous permettrait de faire une inspection d'exécution de la classe donnée et de décider quel comportement utiliser. Évidemment, cela est assez inélégant - et nécessitera une conversion manuelle de la valeur de retour - mais je pense que c'est le mieux que vous puissiez faire. Si vos types génériques sont eux-mêmes génériques, leurs paramètres génériques seront également effacés et leurs classes seront égales, donc cette méthode ne fonctionnera pas.

Cependant, je pointerais également vers la réponse de @ Joachim, cela peut être un cas où vous pouvez diviser le comportement en composants séparés et contourner le problème.


3

Comme vous l'avez vu, vous ne pouvez pas implémenter la même interface deux fois avec des paramètres de type différents (à cause de l'effacement: au moment de l'exécution, ce sont les mêmes interfaces).

De plus, cette approche viole le principe de la responsabilité unique: votre classe doit se concentrer sur le fait d'être Something(quoi que cela signifie) et ne doit pas faire la correspondance avec Aou B en plus de cette tâche.

Il semble que vous devriez vraiment avoir un Mapper<Something,A>et un Mapper<Something,B>. De cette façon, chaque classe a une seule responsabilité clairement définie et vous ne rencontrez pas le problème d'implémenter la même interface deux fois.


Eh bien, l'idée est de laisser la classe se charger de "transformer" son contenu en d'autres objets. Ensuite, il y a un répartiteur qui les gère d'une manière agnostique de classe, donc l'exigence de génériques. Je vais réfléchir un peu à l'extraction de la logique, bien que cela signifie en fait que je divise la classe en deux, mais ils restent étroitement couplés (l'accès au champ doit être accordé, la modification la plupart du temps implique la modification de l'autre, etc.)
estani

@estani: oui, ils sont un peu étroitement couplés, mais ils ont des responsabilités distinctes. Pensez également à ceci: lorsque vous introduisez une nouvelle classe Cet que vous voulez Somethingy être mappable, vous devez alors modifier Something, ce qui est trop de couplage. L'ajout d'un nouveau SoemthingToCMapperest moins intrusif.
Joachim Sauer

1
+1 à ceci - de manière générale, vous devriez privilégier la composition à l'héritage si vous essayez de réaliser ce que l'OP veut (en Java). Java 8 avec des méthodes par défaut rend cela encore plus facile - mais tout le monde ne peut pas encore sauter sur le bord du saignement :-).
Martijn Verburg

0

Étant donné que cela n'est pas autorisé à implémenter plusieurs interfaces, vous pouvez envisager l'utilisation de l'encapsulation. (exemple utilisant java8 +)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

Veuillez vérifier ici pour plus d'informations et d'autres exemples: Comment créer une classe Java qui implémente une interface avec deux types génériques? .


-1
public interface IMappable<S, T> {
    T MapFrom(S source);
}

// T - target
// S - source

Si vous souhaitez mapper User à UserDTO et mapper User à UserViewModel, vous aurez besoin de deux implémentations distinctes. N'empilez pas toute cette logique dans une seule classe - cela n'a pas de sens de le faire.

Mise à jour pour garder Joachim heureux

public interface ITypeConverter<TSource, TDestination>
{
    TDestination Convert(TSource source);
}

Mais maintenant, nous sommes dans le domaine Automapper ( http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters )


Je ne pense pas que ce IMappablesoit un bon nom pour quelque chose qui mappe d'autres choses. Mapper(ou IMapper, si vous devez ;-)) est probablement plus correct. (Au fait: non, ce n'était pas mon downvote).
Joachim Sauer

J'ai pris ce qui était dans la question et préfixé avec un I pour souligner le fait qu'il s'agit d'une interface. Je résout un problème de conception selon la question, par opposition à un problème de dénomination.
CodeART

1
désolé, mais à mon avis, on ne peut pas vraiment "résoudre" un problème de conception et ignorer la dénomination. La conception signifie des structures compréhensibles. Une dénomination incorrecte est un problème de compréhension.
Joachim Sauer

La mise à jour devrait vous
rassurer

1
@CodeART Si j'ai bien compris votre réponse, cela implique que "MapFrom" (qui devrait être en minuscule ;-) crée l'objet. Dans mon cas, il s'agit simplement de remplir des informations sur un objet déjà créé.
estani
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.