Java – Collisione del nome del metodo nell’implementazione dell’interfaccia

Se ho due interfacce, entrambe molto diverse nei loro scopi, ma con la stessa firma del metodo, come faccio a implementare una class entrambe senza essere costretti a scrivere un singolo metodo che serve per entrambe le interfacce e scrivendo qualche logica convoluta nel metodo implementazione che verifica per quale tipo di object viene effettuata la chiamata e invoca il codice corretto?

In C # questo è superato da quella che viene chiamata implementazione esplicita dell’interfaccia. Esiste un modo equivalente in Java?

No, non c’è modo di implementare lo stesso metodo in due modi diversi in una class in Java.

Ciò può portare a molte situazioni confusionarie, ed è per questo che Java lo ha vietato.

interface ISomething { void doSomething(); } interface ISomething2 { void doSomething(); } class Impl implements ISomething, ISomething2 { void doSomething() {} // There can only be one implementation of this method. } 

Quello che puoi fare è comporre una class di due classi che implementano ciascuna un’interfaccia diversa. Quindi quella class avrà il comportamento di entrambe le interfacce.

 class CompositeClass { ISomething class1; ISomething2 class2; void doSomething1(){class1.doSomething();} void doSomething2(){class2.doSomething();} } 

Non c’è un vero modo per risolvere questo in Java. È ansible utilizzare le classi interne come soluzione temporanea:

 interface Alfa { void m(); } interface Beta { void m(); } class AlfaBeta implements Alfa { private int value; public void m() { ++value; } // Alfa.m() public Beta asBeta() { return new Beta(){ public void m() { --value; } // Beta.m() }; } } 

Sebbene non consenta il cast da AlfaBeta a Beta , i downcast sono generalmente malvagi, e se ci si può aspettare che un’istanza Alfa abbia spesso anche un aspetto Beta , e per qualche ragione (di solito l’ottimizzazione è l’unica ragione valida) tu vuoi essere in grado di convertirlo in Beta , potresti creare un’interfaccia secondaria di Alfa con Beta asBeta() .

Se si verifica questo problema, è molto probabile perché si utilizza l’ ereditarietà in cui si dovrebbe utilizzare la delega . Se è necessario fornire due interfacce diverse, anche se simili, per lo stesso modello di dati sottostante, è necessario utilizzare una vista per fornire a basso costo l’accesso ai dati utilizzando un’altra interfaccia.

Per dare un esempio concreto per il secondo caso, supponiamo di voler implementare sia Collection che MyCollection (che non eredita dalla Collection e ha un’interfaccia incompatibile). È ansible fornire una Collection getCollectionView() e MyCollection getMyCollectionView() che forniscono un’implementazione leggera di Collection e MyCollection , utilizzando gli stessi dati sottostanti.

Per il primo caso … supponiamo che tu voglia veramente un array di numeri interi e una serie di stringhe. Invece di ereditare sia da List che da List , dovresti avere un membro di tipo List e un altro membro di tipo List e fare riferimento a questi membri, piuttosto che provare ad ereditare da entrambi. Anche se hai solo bisogno di un elenco di numeri interi, è meglio usare la composizione / delega sull’ereditarietà in questo caso.

Il problema “classico” di Java riguarda anche il mio sviluppo Android …
La ragione sembra essere semplice:
Più framework / librerie che devi usare, più facilmente le cose possono essere fuori controllo …

Nel mio caso, ho una class BootStrapperApp ereditata da android.app.Application ,
mentre la stessa class dovrebbe anche implementare un’interfaccia di piattaforma di un framework MVVM per integrarsi.
La collisione del metodo si è verificata su un metodo getString () , che è annunciato da entrambe le interfacce e dovrebbe avere un’implementazione differenet in diversi contesti.
La soluzione alternativa (ugly..IMO) sta utilizzando una class interna per implementare tutti i metodi della piattaforma , solo a causa di un conflitto di firma del metodo minore … in alcuni casi, tale metodo preso in prestito non è nemmeno usato affatto (ma interessa la semantica del design principale) .
Tendo a convenire che l’indicazione esplicita di contesto / spazio dei nomi in stile C # è utile.

L’unica soluzione che mi è venuta in mente è usare gli oggetti referece su quello che si desidera impiantare interfacce multiple.

ad esempio: supponendo di avere 2 interfacce da implementare

 public interface Framework1Interface { void method(Object o); } 

e

 public interface Framework2Interface { void method(Object o); } 

puoi racchiuderli in due oggetti Facador:

 public class Facador1 implements Framework1Interface { private final ObjectToUse reference; public static Framework1Interface Create(ObjectToUse ref) { return new Facador1(ref); } private Facador1(ObjectToUse refObject) { this.reference = refObject; } @Override public boolean equals(Object obj) { if (obj instanceof Framework1Interface) { return this == obj; } else if (obj instanceof ObjectToUse) { return reference == obj; } return super.equals(obj); } @Override public void method(Object o) { reference.methodForFrameWork1(o); } } 

e

 public class Facador2 implements Framework2Interface { private final ObjectToUse reference; public static Framework2Interface Create(ObjectToUse ref) { return new Facador2(ref); } private Facador2(ObjectToUse refObject) { this.reference = refObject; } @Override public boolean equals(Object obj) { if (obj instanceof Framework2Interface) { return this == obj; } else if (obj instanceof ObjectToUse) { return reference == obj; } return super.equals(obj); } @Override public void method(Object o) { reference.methodForFrameWork2(o); } } 

Alla fine la class che volevi dovrebbe essere qualcosa di simile

 public class ObjectToUse { private Framework1Interface facFramework1Interface; private Framework2Interface facFramework2Interface; public ObjectToUse() { } public Framework1Interface getAsFramework1Interface() { if (facFramework1Interface == null) { facFramework1Interface = Facador1.Create(this); } return facFramework1Interface; } public Framework2Interface getAsFramework2Interface() { if (facFramework2Interface == null) { facFramework2Interface = Facador2.Create(this); } return facFramework2Interface; } public void methodForFrameWork1(Object o) { } public void methodForFrameWork2(Object o) { } } 

ora puoi usare i metodi getAs * per “esporre” la tua class

È ansible utilizzare un modello dell’adattatore per farlo funzionare. Crea due adattatori per ogni interfaccia e usali. Dovrebbe risolvere il problema.

Tutto bene quando hai il controllo totale su tutto il codice in questione e puoi implementarlo in anticipo. Ora immagina di avere una class pubblica esistente usata in molti posti con un metodo

 public class MyClass{ private String name; MyClass(String name){ this.name = name; } public String getName(){ return name; } } 

Ora è necessario passarlo nel WizzBangProcessor, che richiede classi per implementare il WBPInterface … che ha anche un metodo getName (), ma invece dell’implementazione concreta, questa interfaccia si aspetta che il metodo restituisca il nome di un tipo di Wizz Bang Processing.

In C # sarebbe un trvial

 public class MyClass : WBPInterface{ private String name; String WBPInterface.getName(){ return "MyWizzBangProcessor"; } MyClass(String name){ this.name = name; } public String getName(){ return name; } } 

In Java Tough dovrai identificare ogni punto della base di codice distribuita esistente dove devi convertire da un’interfaccia all’altra. Certo, la società WizzBangProcessor avrebbe dovuto usare getWizzBangProcessName (), ma sono anche sviluppatori. Nel loro contesto, getName andava bene. In realtà, al di fuori di Java, la maggior parte delle altre lingue basate su OO supporta questo. Java è raro nel forzare l’implementazione di tutte le interfacce con lo stesso metodo NAME.

La maggior parte degli altri linguaggi ha un compilatore che è più che felice di prendere un’istruzione per dire “questo metodo in questa class che corrisponde alla firma di questo metodo in questa interfaccia implementata è la sua implementazione”. Dopotutto, l’intero punto di definizione delle interfacce è di permettere che la definizione sia astratta dall’implementazione. (Non ho nemmeno iniziato a utilizzare i metodi predefiniti in Interfacce in Java, per non parlare della sovrascrittura predefinita … perché sicuramente, ogni componente progettato per una macchina da strada dovrebbe essere in grado di essere sbattuto in una macchina volante e basta lavorare – hey sono entrambe le macchine … Sono sicuro che la funzionalità predefinita di dire che il tuo navigatore satellitare non sarà influenzato dagli ingressi pitch and roll di default, perché le auto solo imbardate!