2010-06-24 15 views
26

Supponiamo che io sono queste interfacce:Usando il parametro che implementa più interfacce pre-generici

public interface I1 { 
    void foo(); 
} 

public interface I2 { 
    void bar(); 
} 

e le classi:

public class A extends AParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class B extends BParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class C extends CParent implements I1 { 
    // code for foo method here 
} 

Ora, con i generici posso avere un metodo come:

public <T extends I1 & I2> void method(T param) { 
    param.foo(); 
    param.bar(); 
} 

e posso chiamarlo sia con A che con B come parametri, ma non con C (non implementa I2).

Esiste un modo per ottenere questo tipo di pre generici di sicurezza (java < 1.5).

Si consideri che A, B e C hanno alberi di ereditarietà diversi e non è davvero un'opzione per fare qualcosa come AParent e BParent che hanno un genitore comune loro stessi.

So che si possa fare:

public void method(I1 param) { 
    param.foo(); 
    ((I2)param).bar(); 
} 

ma allora si potrebbe anche chiamare method(new C()) che non implementa I2, in modo da ottenere nei guai.

Quindi ci sono altri modi in cui potreste averlo fatto?

P.S. : Non ho davvero bisogno di farlo, è soprattutto per curiosità che chiedo.

risposta

20

Creare una terza interfaccia I3 estende I1 e I2. Quindi le classi A e B implementano entrambi I3 e il metodo generico accetta I3.

Questo è forse l'unico modo per farlo.

+4

I sorta di realizzato questo dopo ho fatto la domanda, anche se si dispone di più di due interfacce, e si desidera combinare quelli per metodi diversi, può diventare un dolore che crea un'interfaccia child per ogni combinazione che si desidera utilizzare. Stavo pensando più ad un metodo come 'method (I1 & I2 param)' o qualche altro modo per dire che il parametro implementa più interfacce in movimento (senza creare una nuova classe/interfaccia) –

+0

Non penso questo è un buon punto di vista del design. Per favore vedi la mia risposta. – hqt

2

Prima di Java 1.5 non esiste alcuna soluzione IMO per ottenere tale sicurezza di tipo in fase di compilazione. Ma c'è un'anima in fase di esecuzione con "instanceof".

public void method(Object o) { 
    if (!(o instanceof I1)) 
    throw new RuntimeException("o is not instance of I1"); 

    if (!(o instanceof I2)) 
    throw new RuntimeException("o is not instance of I2"); 

    // go ahead ... 
} 
+0

Beh, se chiami 'method (new C())' per il mio esempio non generico nella domanda otterrai 'ClassCastException' quando esegui il cast di' I2' per chiamare 'bar()', quindi non c'è molto miglioramento Qui. –

+0

No, prima riceverai una RuntimeException, perché il controllo di "(o instanceof I2)" è falso;) Beh, immagino che il suggerimento di sri sia forse molto meglio :) Perché non avrai il rischio di un eccezione di runtime come con il mio suggerimento perché il compilatore fallirà prima. – VuuRWerK

2

sri è la risposta migliore se si ha il permesso di modificare la firma di A e B. Tuttavia, se non hai il permesso, allora avresti potuto fare:

public void method(I1 param1 , I2 param2) { // unpopular classes that do not implement I3 must use this method 
    param1.foo(); 
    param2.bar(); 
} 
public void method(I3 param){ // I hope everybody implements I3 when appropriate 
    param.foo(); 
    param.bar(); 
} 
public void method(A param){// A is a popular class 
    method(param,param); 
} 
public void method(B param){// B is a popular class 
    method(param,param); 
} 

Naturalmente, ora basta usare i generici.

+0

Sì, funzionerebbe, ma se I1 è simile a Comparable, I2 qualcosa come Serializable che sono ampiamente utilizzati, avendo un metodo per ogni Type che implementa entrambi (che chiama quello privato) potrebbe essere un po 'una sfida. Quando ho pensato alla domanda, ho solo dato A e B come esempi, non mi interesserei davvero cosa sono quando chiamano il metodo (l'unica cosa che importava era che implementassero quelle interfacce). E infatti se 'method()' potrebbe essere un metodo API, esposto per altri da usare, codificare con difficoltà le classi che possono usarlo in quel modo sarebbe un problema. –

+0

Quindi una scelta migliore sarebbe rendere pubblico il metodo privato e quindi alcuni metodi di convenienza per le classi di maggior interesse, che potrebbero includere l'interfaccia I3 dell'interfaccia di sri. – emory

6

Non credo che le risposte precedenti siano valide sotto il punto di vista del design. Quando si crea un'interfaccia, si desidera assicurarsi che l'oggetto chiamante abbia la responsabilità per alcune azioni definite in tale interfaccia. Quindi ci sono due soluzioni di cui sopra, e dirò perché quelle soluzioni non sono buone in termini di design.

1. make un'interfaccia estende entrambe le due interfacce:

public interface IC extends IA,IB { 
    // empty method here 
} 

Sopra codice è non-senso. Si definisce un'interfaccia separata solo per combinare altre interfacce e all'interno non ci sono nuovi metodi. Non si aggiunge alcun valore a IC anziché combinare due interfacce IA e IB. E alcune situazioni, in questo modo renderanno il tuo codice "un po 'divertente" quando non riesci a trovare il nome adatto per la terza interfaccia.Questa situazione porterà ad un nome di interfaccia come IReadableAndWriteable o ISomethingAndSomethingAndAnotherThing

2. tipo colato metodo all'interno:

public void methodA(IA object) { 
    if (object instance of IB) { 
    ((IB)(object)).someMethod() 
} 

questo modo ha senso troppo. Perché il parametro di input è IA quindi è necessario eseguire qualche azione dall'interfaccia IB? Sotto il punto di vista del programmatore, non c'è modo di saperlo se non leggendo il tuo documento. Non va bene per progettare una funzione per l'uso di altre persone.

vera soluzione:

soluzioni di cui sopra c'è un altro problema nel design: È vigore uso programmatore un oggetto che ha la responsabilità di due interfacce. Qual è il problema se il programmatore non vuole farlo. Vogliono utilizzare due diverse classi di calcestruzzo per ogni interfaccia diversa per test più semplici, codice pulito? Non possono.

Si dovrebbe fare due parametri diversi nella tua firma del metodo:

public void exampleMethod(IA object1, IB object2) { 
    object1.someMethod() 
    object2.someMethod() 
} 

E per chiamare metodo di cui sopra, si mette stesso parametro interno (se stesso oggetto di uso programmatore). Possono anche mettere oggetti diversi.

public void caller() { 
    IC object3 = new C(); // C is the class implements both IA and IB 
    exampleMethod(object3, object3); 
    IA objectA = new A(); 
    IB objectB = new B(); 
    exampleMethod(objectA, objectB);  
} 

Spero che questo aiuto :)

+2

Mi spiace di svegliare un vecchio argomento, ma penso che ne valga la pena. Penso davvero che questa risposta sia la migliore.Tuttavia, per il punto 1, indipendentemente dal fatto che abbia senso è piuttosto soggettivo (può avere senso come soluzione temporanea, almeno), ma c'è un caso veramente pratico e oggettivo che dimostra che non è un buon progetto: se si implementa una classe che estende entrambe le interfacce rilevanti ma * non * l'interfaccia che le combina, quindi non è possibile utilizzarla in un metodo che richiede l'interfaccia di combinazione, sebbene rispetti il ​​contratto iniziale. – Matthieu

+1

Non riesco a modificare la risposta precedente, quindi ne aggiungo un'altra: l'idea di creare un'interfaccia combinata non è buona o incompleta. Non dovrebbe essere una semplice interfaccia, ma un wrapper: se ottieni l'implementazione da una libreria esterna, ma vuoi che qualsiasi implementazione delle due interfacce sia utilizzabile, quindi non vuoi essere limitata dalla lib, allora hai bisogno un involucro o un adattatore se preferisci. Questa soluzione garantisce di avere entrambe le interfacce associate alla stessa istanza, mentre il post di hqt sopra non consente di garantire tale vincolo, che potrebbe comunque valere. – Matthieu

Problemi correlati