2009-04-15 1 views
5

Penso che quanto segue non possa essere fatto in Java. Ma sarei felice di imparare come implementare qualcosa che gli somigli.implementazione di interfacce dopo il fatto

Supponiamo di avere una classe C, che è già utilizzata nel codice compilato. (Non possiamo né cambiare quel codice né la definizione originale di C).

Supponiamo inoltre che ci sia un codice interessante che potrebbe essere riutilizzato, se solo C implementasse l'interfaccia I. È, infatti, più o meno banale derivare D che è solo C + l'implementazione dei metodi di interfaccia.

Eppure, sembra che ci sia alcun modo, una volta che ho una C, per dire: io voglio che tu sia un D, ​​vale a dire, un C attuazione I.

(osservazione laterale: Credo che il cast (D) c, dove c è il tipo di runtime C, l'unica differenza rispetto a C sono metodi aggiunti, che dovrebbe essere sicuro, non dovrebbe?)

Come si può aggirare questo calamità?

(Conosco il modello di progettazione di fabbrica, ma questa non è una soluzione, a quanto pare.Perché, una volta che riusciamo a creare D in tutti i luoghi in cui erano precedentemente C, qualcun altro trova un'altra interfaccia J utile e deriva E estende C implementa J. Ma E e D sono incompatibili, dal momento che entrambi aggiungono un diverso insieme di metodi a C. Quindi, mentre possiamo sempre passare una E dove ci si aspetta una C, non possiamo passare una E dove ci si aspetta una D. Piuttosto, ora, avremmo bisogno di una nuova classe F estende le implementazioni C I, J.)

risposta

7

Se tutto quello che serve per essere compatibile con sia le interfacce allora nessun problema dai un'occhiata a dynamic proxy classes, in pratica come implementare le interfacce in runtime in java.

se è necessaria una compatibilità runtime simile con le classi, suggerisco di dare un'occhiata alle librerie opensource cglib o javaassist.

+0

Questo sembra abbastanza complessa, ma io sicuramente fare un tentativo. Puoi dire qualcosa sul costo di runtime, cioè mi sembra di aver bisogno di un'istanza del gestore di richiamo extra per oggetto. – Ingo

+1

Il metodo delegato sarebbe molto più semplice, questo aggiunge complessità senza alcun beneficio per questo particolare problema. – Robin

+0

pensavo che fosse necessaria un'altra soluzione oltre all'applicazione del modello dell'adattatore specificamente per una soluzione runtime. forse un'errata interpretazione – MahdeTo

10

Non potresti usare una classe delegata, cioè una nuova classe che avvolge un'istanza di "Classe C", ma anche implementa "Interfaccia I"?

public class D implements I { 

    private C c; 

    public D (C _c) { 
     this.c = _c; 
    } 

    public void method_from_class_C() { 
     c.method_from_class_C(); 
    } 
    // repeat ad-nauseum for all of class C's public methods 
    ... 

    public void method_from_interface_I() { 
     // does stuff 
    } 
    // and do the same for all of interface I's methods too 
} 

e poi, se è necessario richiamare una funzione che normalmente richiede un parametro di tipo I solo fare questo:

result = some_function(new D(c)); 
+0

perché non semplicemente "Classe D estende C implementa I"? – dfa

+0

perché richiede di costruire con 'nuova D (...)' invece di 'nuova C (...)', che potrebbe non essere possibile. Non è nemmeno possibile fare "D d = (D) c" anche se D estende C. Downcasts da D a C dovrebbe essere possibile, però. – Alnitak

+0

Ho provato a spiegarlo prima. Ottengo C da codice non sotto il mio controllo e voglio passarli (non un nuovo/diverso oggetto) al codice che funziona con le interfacce. – Ingo

3

Se (è possibile) gestire il ClassLoader che carica la classe C, allora si può provare a eseguire alcuni shenanigans del tempo di caricamento della classe con la strumentazione bytecode per far sì che la classe implementa l'interfaccia.

Lo stesso può essere fatto durante il tempo di costruzione, ovviamente. Potrebbe anche essere più semplice in questo modo (dato che non è necessario accedere a ClassLoader).

1

Credo che quello che vuoi sia possibile usando java.lang.reflect.Proxy; in effetti ho fatto qualcosa di simile per un progetto in corso. Tuttavia, è un bel po 'di lavoro, e gli "oggetti ibridi" risultanti possono esporre un comportamento strano (perché i richiami di metodo su di essi vengono indirizzati a diversi oggetti concreti, ci sono problemi quando questi metodi cercano di chiamarsi l'un l'altro).

2

(osservazione laterale: Penso che il cast (D) c, dove tipo di runtime di c è C, deve essere consentito se D è un C e l'unica differenza di C sono metodi aggiunti Questo dovrebbe. sii sicuro, non dovrebbe?)

Niente affatto. Se potessi fare questo cast, allora potresti compilare il codice che tentava di chiamare uno dei "metodi aggiunti" su questo oggetto, che fallirebbe in fase di esecuzione poiché tale metodo non esiste in C.

Penso che tu stia immaginando che il cast rilevi i metodi "mancanti" da C e li delegano automaticamente a D. Dubito che sarebbe fattibile, anche se non posso parlare alle implicazioni del design del linguaggio.

Mi sembra la soluzione al vostro problema è:

Definire la classe D, che si estende C e implementa I
definire un costruttore D (C c), che clona essenzialmente lo stato del dato oggetto C in un nuovo oggetto D.
L'oggetto D può essere passato al codice esistente perché è un C e può essere passato al codice che desidera un I perché è un I

+0

I significava (D) c per cambiare effettivamente il tipo di runtime C a D (semplicemente sovrascrivendo vtable-puntatore, per esempio). Naturalmente, ciò dovrebbe essere possibile solo se il compilatore può dimostrare che un cast di esecuzione da C a D non modifica il comportamento di Cish dell'oggetto, ma aggiunge semplicemente nuove funzionalità. – Ingo

+0

Normalmente è possibile solo il downcast (cioè da una sottoclasse alla sua superclasse) perché tale _removes_ funzionalità e membri dei dati. – Alnitak

0

Penso che tu non possa farlo perché Java è rigorosamente digitato Credo che possa essere fatto in linguaggi come Ruby e Python con l'uso di mixin.

Per quanto riguarda Java sembra decisamente come un buon utilizzo per il modello di progettazione adattatore (era già proposto in precedenza come un oggetto "wrapper").