Implementazione di più interfacce generiche in Java


10

Ho bisogno di un'interfaccia che mi assicuri che sia disponibile un certo metodo, inclusa una firma specifica. Finora è quello che ho:

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

Il problema sorge quando una classe dovrebbe essere mappabile su più altre entità. Il caso ideale sarebbe questo (non Java):

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

Quale sarebbe il modo migliore per ottenere questo rimanendo il più "generico" possibile?

Risposte:


10

Questo, ovviamente, non è qualcosa che puoi fare a causa della cancellazione del tipo . In fase di esecuzione, hai due metodi public Object mapTo(Object), che ovviamente non possono coesistere.

Sfortunatamente, ciò che stai cercando di fare è semplicemente al di là del sistema di tipi di Java.

Supponendo che il tuo tipo generico sia sempre un tipo di prima classe, e non di per sé generico, potresti ottenere un comportamento simile verso l'esterno disponendo del metodo mapTo(Object, Class), che ti permetterebbe di fare un'ispezione di runtime della classe data e decidere quale comportamento usare. Ovviamente questo è piuttosto inelegante - e richiederà il cast manuale del valore di ritorno - ma penso che sia il meglio che puoi fare. Se i tuoi tipi generici sono essi stessi generici, anche i loro parametri generici verranno cancellati e le loro Classi saranno uguali, quindi questo metodo non funzionerà.

Tuttavia, vorrei sottolineare anche la risposta di @ Joachim, questo potrebbe essere un caso in cui è possibile dividere il comportamento in componenti separati e eludere l'intero problema.


3

Come hai visto, non è possibile implementare la stessa interfaccia due volte con parametri di tipo diverso (a causa della cancellazione: in fase di esecuzione sono le stesse interfacce).

Inoltre, questo approccio viola il principio della singola responsabilità: la tua classe dovrebbe concentrarsi sull'essere un Something(qualunque cosa significhi) e non dovrebbe fare la mappatura Ao B in aggiunta a tale compito.

Sembra che dovresti davvero avere un Mapper<Something,A>e un Mapper<Something,B>. In questo modo ogni classe ha un'unica responsabilità chiaramente definita e non si incontra il problema di implementare la stessa interfaccia due volte.


Bene, l'idea è di lasciare che la classe sia responsabile della "trasformazione" dei suoi contenuti in altri oggetti. Poi c'è un dispatcher che li gestisce in modo agnostico di classe, quindi il requisito generici. Ci penserò un po 'sull'estrazione della logica, anche se questo in realtà significa che sto dividendo la classe in due, ma rimangono strettamente accoppiati (l'accesso al campo dovrebbe essere concesso, modificando uno il più delle volte implica modificando l'altro, ecc.)
estani,

@estani: sì, sono in qualche modo strettamente accoppiati, ma hanno responsabilità distinte. Pensa anche a questo: quando introduci una nuova classe Ce vuoi Somethingessere mappabile a quella, allora dovresti modificare Something, il che è troppo accoppiamento. Basta aggiungere un nuovo SoemthingToCMapperè meno invadente.
Joachim Sauer,

1
+1 a questo - in generale dovresti favorire la composizione rispetto all'eredità se stai cercando di ottenere ciò che l'OP vuole (in Java). Java 8 con i metodi predefiniti lo rende ancora più semplice, ma non tutti possono ancora saltare al limite :-).
Martijn Verburg,

0

Dato che non è consentito implementare interfacce multiple, puoi considerare l'uso dell'incapsulamento. (esempio usando 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);
}

Per ulteriori informazioni e ulteriori esempi, consultare qui: Come creare una classe Java che implementa un'interfaccia con due tipi generici? .


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

// T - target
// S - source

Se si desidera mappare l'utente a UserDTO e mappare l'utente a UserViewModel, sono necessarie due implementazioni separate. Non accumulare tutta questa logica in una singola classe - non ha senso farlo.

Aggiornamento per rendere felice Joachim

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

Ma ora siamo nel regno di Automapper ( http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters )


Non credo IMappablesia un buon nome per qualcosa che mappa altre cose. Mapper(o IMapper, se è necessario ;-)) è probabilmente più corretto. (A proposito: no, quello non era il mio voto negativo).
Joachim Sauer,

Ho preso ciò che era nella domanda e ho anteposto un I per evidenziare il fatto che si tratta di un'interfaccia. Sto risolvendo il problema di progettazione come da domanda, al contrario di un problema di denominazione.
CodeART

1
scusate, ma secondo me non si può davvero "risolvere" un problema di progettazione e ignorare la denominazione. Design significa strutture comprensibili. La denominazione errata è un problema da comprendere.
Joachim Sauer,

L'aggiornamento dovrebbe farti riposare la mente ;-)
CodeART

1
@CodeART Se ho capito bene la tua risposta, implica "MapFrom" (che dovrebbe essere in minuscolo ;-) crea l'oggetto. Nel mio caso sta solo riempiendo informazioni su un oggetto già creato.
estani,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.