Jeśli mam dwa interfejsy, oba całkiem różne w swoich celach, ale z tą samą sygnaturą metody, jak zrobić klasę implementującą obie bez konieczności pisania jednej metody, która służy do obu interfejsów i pisania zawiłej logiki w metodzie implementacja, która sprawdza, dla jakiego typu obiektu jest wykonywane wywołanie i wywołuje odpowiedni kod?
W języku C # można to obejść przez tak zwaną jawną implementację interfejsu. Czy jest jakiś równoważny sposób w Javie?
Odpowiedzi:
Nie, nie ma możliwości zaimplementowania tej samej metody na dwa różne sposoby w jednej klasie w Javie.
Może to prowadzić do wielu zagmatwanych sytuacji, dlatego Java na to nie zezwala.
interface ISomething { void doSomething(); } interface ISomething2 { void doSomething(); } class Impl implements ISomething, ISomething2 { void doSomething() {} // There can only be one implementation of this method. }
Możesz stworzyć klasę z dwóch klas, z których każda implementuje inny interfejs. Wtedy ta jedna klasa będzie miała zachowanie obu interfejsów.
class CompositeClass { ISomething class1; ISomething2 class2; void doSomething1(){class1.doSomething();} void doSomething2(){class2.doSomething();} }
źródło
ISomething1 CompositeClass.asInterface1();
iISomething2 CompositeClass.asInterface2();
do tej klasy. Następnie możesz po prostu uzyskać jedną lub drugą z klasy złożonej. Nie ma jednak dobrego rozwiązania tego problemu.public long getCountAsLong() implements interface2.getCount {...}
[w przypadku, gdy interfejs wymaga a,long
ale użytkownicy klasy oczekująint
] lubprivate void AddStub(T newObj) implements coolectionInterface.Add
[zakładając, żecollectionInterface
macanAdd()
metodę i dla wszystkich instancji tej klasy zwracafalse
]?Nie ma prawdziwego sposobu na rozwiązanie tego problemu w Javie. Możesz użyć klas wewnętrznych jako obejścia:
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() }; } }
Chociaż nie pozwala na rzucanie od
AlfaBeta
doBeta
, spadki są generalnie złe i jeśli można się spodziewać, żeAlfa
instancja często ma równieżBeta
aspekt iz jakiegoś powodu (zwykle optymalizacja jest jedynym ważnym powodem), chcesz mieć możliwość aby przekonwertować go naBeta
, możesz zrobić podinterfejsAlfa
zBeta asBeta()
in it.źródło
Jeśli napotkasz ten problem, najprawdopodobniej jest to spowodowane dziedziczeniem, w którym powinieneś używać delegowania . Jeśli potrzebujesz zapewnić dwa różne, aczkolwiek podobne interfejsy dla tego samego podstawowego modelu danych, powinieneś użyć widoku, aby tanio zapewnić dostęp do danych za pomocą innego interfejsu.
Aby podać konkretny przykład tego drugiego przypadku, załóżmy, że chcesz zaimplementować zarówno
Collection
iMyCollection
(co nie dziedziczy poCollection
i ma niekompatybilny interfejs). Możesz dostarczyć aCollection getCollectionView()
iMyCollection getMyCollectionView()
funkcje, które zapewniają lekką implementacjęCollection
iMyCollection
przy użyciu tych samych danych bazowych.W pierwszym przypadku ... przypuśćmy, że naprawdę potrzebujesz tablicy liczb całkowitych i tablicy ciągów. Zamiast dziedziczyć po obu
List<Integer>
iList<String>
, powinieneś mieć jeden element członkowski typuList<Integer>
i inny element członkowski typuList<String>
i odwoływać się do tych elementów członkowskich, zamiast próbować dziedziczyć z obu. Nawet jeśli potrzebna byłaby tylko lista liczb całkowitych, w tym przypadku lepiej jest użyć kompozycji / delegacji zamiast dziedziczenia.źródło
"Klasyczny" problem z Javą wpływa również na mój programowanie w Androidzie ...
Powód wydaje się być prosty:
więcej frameworków / bibliotek, których musisz użyć, łatwiej rzeczy mogą wymknąć się spod kontroli ...
W moim przypadku mam klasę BootStrapperApp dziedziczony z android.app.Application ,
podczas gdy ta sama klasa powinna również implementować interfejs platformy frameworka MVVM w celu integracji.
Wystąpiła kolizja metod w metodzie getString () , która jest ogłaszana przez oba interfejsy i powinna mieć różną implementację w różnych kontekstach.
Obejście problemu (brzydkie..IMO) polega na użyciu klasy wewnętrznej do zaimplementowania całej platformymetody, tylko z powodu jednego pomniejszego konfliktu sygnatur metod ... w niektórych przypadkach taka pożyczona metoda nie jest nawet używana (ale wpływa na semantykę głównego projektu).
Zwykle zgadzam się, że wyraźne wskazanie kontekstu / przestrzeni nazw w stylu C # jest pomocne.
źródło
Jedynym rozwiązaniem, które przyszło mi do głowy, jest użycie obiektów referencyjnych do tego, który chcesz zaimplementować wiele interfejsów.
np .: zakładając, że masz 2 interfejsy do zaimplementowania
public interface Framework1Interface { void method(Object o); }
i
public interface Framework2Interface { void method(Object o); }
możesz zamknąć je w dwóch obiektach 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); } }
i
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); } }
W końcu klasa, którą chciałeś, powinna coś podobnego
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) { } }
możesz teraz użyć metod getAs * do „ujawnienia” swojej klasy
źródło
Możesz użyć wzorca adaptera, aby te działały. Utwórz dwa adaptery dla każdego interfejsu i użyj go. To powinno rozwiązać problem.
źródło
Wszystko dobrze, gdy masz całkowitą kontrolę nad całym kodem, o którym mowa, i możesz to zaimplementować z góry. Teraz wyobraź sobie, że masz istniejącą klasę publiczną używaną w wielu miejscach za pomocą metody
public class MyClass{ private String name; MyClass(String name){ this.name = name; } public String getName(){ return name; } }
Teraz musisz przekazać go do standardowego WizzBangProcessor, który wymaga klas do implementacji WBPInterface ... który również ma metodę getName (), ale zamiast konkretnej implementacji ten interfejs oczekuje, że metoda zwróci nazwę typu przetwarzania Wizz Bang.
W C # byłaby to trywialna
public class MyClass : WBPInterface{ private String name; String WBPInterface.getName(){ return "MyWizzBangProcessor"; } MyClass(String name){ this.name = name; } public String getName(){ return name; } }
W Javie Tough będziesz musiał zidentyfikować każdy punkt w istniejącej wdrożonej bazie kodu, w którym musisz dokonać konwersji z jednego interfejsu na drugi. Jasne, firma WizzBangProcessor powinna była użyć metody getWizzBangProcessName (), ale są też programistami. W ich kontekście getName było w porządku. W rzeczywistości poza Javą większość innych języków opartych na obiektach obiektowych obsługuje to. Java rzadko wymusza implementację wszystkich interfejsów za pomocą tej samej metody NAME.
Większość innych języków ma kompilator, który z przyjemnością przyjmuje instrukcję mówiącą „ta metoda w tej klasie, która pasuje do sygnatury tej metody w tym zaimplementowanym interfejsie, jest jej implementacją”. W końcu celem definiowania interfejsów jest umożliwienie wyodrębnienia definicji z implementacji. (Nawet nie zaczynaj od posiadania domyślnych metod w interfejsach w Javie, nie mówiąc już o domyślnym zastępowaniu ... ponieważ z pewnością każdy komponent zaprojektowany dla samochodu drogowego powinien być w stanie uderzyć w latający samochód i po prostu działać - hej oba są samochodami ... Jestem pewien, że domyślna funkcjonalność, powiedzmy, twojego urządzenia nawigacyjnego nie będzie miała wpływu na domyślne ustawienia nachylenia i przechylenia, ponieważ samochody tylko odchylają się!
źródło