Wiem, że nie można zdefiniować konstruktora w interfejsie. Ale zastanawiam się, dlaczego, bo myślę, że może być bardzo przydatne.
Więc możesz być pewien, że niektóre pola w klasie są zdefiniowane dla każdej implementacji tego interfejsu.
Weźmy na przykład pod uwagę następującą klasę wiadomości:
public class MyMessage {
public MyMessage(String receiver) {
this.receiver = receiver;
}
private String receiver;
public void send() {
//some implementation for sending the mssage to the receiver
}
}
Jeśli zdefiniujesz interfejs dla tej klasy, abym mógł mieć więcej klas, które implementują interfejs wiadomości, mogę zdefiniować tylko metodę wysyłania, a nie konstruktora. Jak więc mogę zapewnić, że każda implementacja tej klasy naprawdę ma ustawiony odbiornik? Jeśli używam metody takiej setReceiver(String receiver)
, jak nie mam pewności, czy ta metoda jest naprawdę wywołana. W konstruktorze mogłem to zapewnić.
Odpowiedzi:
Biorąc niektóre z rzeczy, które opisałeś:
... te wymagania są dokładnie tym, do czego służą klasy abstrakcyjne .
źródło
Message
interfejs, który definiujesend()
metodę, a jeśli Sebi chce udostępnić klasę „bazową” dla implementacjiMessage
interfejsu, podajAbstractMessage
również. Klasy abstrakcyjne nie powinny zastępować interfejsów, nigdy nie próbowałem tego sugerować.Problem, który pojawia się, gdy zezwalasz na konstruktory w interfejsach, wynika z możliwości zaimplementowania kilku interfejsów w tym samym czasie. Gdy klasa implementuje kilka interfejsów, które definiują różne konstruktory, klasa musiałaby zaimplementować kilka konstruktorów, z których każdy spełnia tylko jeden interfejs, ale nie obsługuje innych. Niemożliwe będzie utworzenie obiektu wywołującego każdy z tych konstruktorów.
Lub w kodzie:
źródło
class A implements Named, HashList { A(){HashList(new list()); Named("name");} }
new Set<Fnord>()
interpretowanie go jako „Daj mi coś, czego mogę użyć jakoSet<Fnord>
”; jeśli autorSet<T>
zamierzałHashSet<T>
być implementacją do rzeczy, które nie mają szczególnej potrzeby czegoś innego, interfejs mógłby wtedy zdefiniowaćnew Set<Fnord>()
jako synonimnew HashSet<Fnord>()
. Implementacja wielu interfejsów przez klasę nie stanowiłaby żadnego problemu, ponieważnew InterfaceName()
po prostu skonstruowałaby klasę wyznaczoną przez interfejs .A(String,List)
konstruktor może być wyznaczonym konstruktoremA(String)
iA(List)
może być drugorzędnym konstruktorem, który go wywołuje. Twój kod nie jest kontrprzykładem, tylko kiepskim.Interfejs definiuje kontrakt na API, czyli zestaw metod, na które zgadzają się zarówno implementujący jak i użytkownik API. Interfejs nie ma implementacji instancji, dlatego nie ma konstruktora.
Opisany przypadek użycia jest podobny do klasy abstrakcyjnej, w której konstruktor wywołuje metodę metody abstrakcyjnej zaimplementowaną w klasie potomnej.
Nieodłącznym problemem jest tutaj to, że podczas wykonywania konstruktora podstawowego obiekt potomny nie jest jeszcze skonstruowany, a zatem znajduje się w nieprzewidywalnym stanie.
Podsumowując: czy prosi się o kłopoty, gdy wywołujesz przeciążone metody z konstruktorów nadrzędnych, aby zacytować mindprod :
źródło
Obejściem, które możesz wypróbować, jest zdefiniowanie
getInstance()
metody w interfejsie, aby osoba wdrażająca była świadoma, jakie parametry muszą być obsługiwane. Nie jest tak solidna jak klasa abstrakcyjna, ale zapewnia większą elastyczność jako interfejs.Jednak to obejście wymaga użycia programu
getInstance()
do utworzenia wystąpienia wszystkich obiektów tego interfejsu.Na przykład
źródło
W interfejsie są tylko pola statyczne, które nie wymagają inicjalizacji podczas tworzenia obiektu w podklasie, a metoda interfejsu musi zapewniać rzeczywistą implementację w podklasie, więc nie ma potrzeby konstruktora w interfejsie.
Drugi powód - podczas tworzenia obiektu podklasy wywoływany jest konstruktor nadrzędny, ale jeśli będzie zaimplementowanych więcej niż jeden interfejs, to podczas wywołania konstruktora interfejsu wystąpi konflikt dotyczący tego, który konstruktor interfejsu wywoła pierwszy
źródło
Jeśli chcesz mieć pewność, że każda implementacja interfejsu zawiera określone pole, wystarczy dodać do interfejsu funkcję pobierającą dla tego pola :
Receiver
obiekt musi zostać w jakiś sposób przekazany do klasy (przez konstruktora lub ustawiającego)źródło
Zależności, do których nie istnieją odniesienia w metodach interfejsów, należy traktować jako szczegóły implementacji, a nie coś, co wymusza interfejs. Oczywiście mogą istnieć wyjątki, ale z reguły należy zdefiniować interfejs zgodnie z oczekiwanym zachowaniem. Stan wewnętrzny danej implementacji nie powinien być problemem projektowym interfejsu.
źródło
Zobacz to pytanie, aby dowiedzieć się, dlaczego (zaczerpnięte z komentarzy).
Jeśli naprawdę potrzebujesz zrobić coś takiego, możesz potrzebować abstrakcyjnej klasy bazowej zamiast interfejsu.
źródło
Dzieje się tak, ponieważ interfejsy nie pozwalają na zdefiniowanie w niej treści metody. Ale powinniśmy zdefiniować konstruktor w tej samej klasie, co interfejsy mają domyślnie abstrakcyjny modyfikator dla wszystkich metod do zdefiniowania. Dlatego nie możemy zdefiniować konstruktora w interfejsach.
źródło
Oto przykład użycia tego Technic. W tym konkretnym przykładzie kod wywołuje Firebase przy użyciu makiety
MyCompletionListener
, czyli interfejsu zamaskowanego jako klasa abstrakcyjna, interfejsu z konstruktoremźródło
private
modyfikatora dostępu zinterface
?Generalnie konstruktory służą do inicjowania niestatycznych elementów członkowskich określonej klasy w odniesieniu do obiektu.
Nie ma tworzenia obiektów dla interfejsu, ponieważ istnieją tylko zadeklarowane metody, ale nie są zdefiniowane. Dlaczego nie możemy utworzyć obiektu zgodnie z zadeklarowanymi metodami, tworzenie obiektu jest niczym innym, jak przydzieleniem części pamięci (w pamięci sterty) dla niestatycznych elementów członkowskich.
JVM utworzy pamięć dla członków, które są w pełni rozwinięte i gotowe do użycia, na podstawie których JVM oblicza, ile pamięci jest dla nich wymagane i tworzy pamięć.
W przypadku zadeklarowanych metod JVM nie jest w stanie obliczyć ilości pamięci wymaganej dla tych zadeklarowanych metod, ponieważ implementacja będzie w przyszłości, co nie zostanie wykonane do tego czasu. więc tworzenie obiektów nie jest możliwe dla interfejsu.
wniosek:
bez tworzenia obiektu nie ma szans na zainicjowanie niestatycznych składowych za pomocą konstruktora, dlatego konstruktor nie jest dozwolony wewnątrz interfejsu (ponieważ nie ma użycia konstruktora wewnątrz interfejsu)
źródło