Uczono mnie, że interfejs Marker w Javie jest pustym interfejsem i służy do sygnalizowania kompilatorowi lub JVM, że obiekty klasy implementującej ten interfejs muszą być traktowane w specjalny sposób, jak serializacja, klonowanie itp.
Ale ostatnio dowiedziałem się, że tak naprawdę nie ma to nic wspólnego z kompilatorem ani maszyną JVM. Na przykład, w przypadku Serializable
interfejsu metoda writeObject(Object)
of ObjectOutputStream
robi coś w rodzaju instanceOf Serializable
wykrycia, czy klasa implementuje Serializable
& generuje NotSerializableException
odpowiednio. Wszystko jest obsługiwane w kodzie i wydaje się, że jest to wzorzec projektowy, więc myślę, że możemy zdefiniować nasze własne interfejsy znaczników.
Teraz moje wątpliwości:
Czy definicja interfejsu znacznika wspomniana powyżej w punkcie 1 jest błędna? Jak zatem możemy zdefiniować interfejs Markera?
I zamiast używać
instanceOf
operatora, dlaczego metoda nie może być podobna dowriteObject(Serializable)
sprawdzania typu w czasie kompilacji, a nie w czasie wykonywania?W jaki sposób adnotacje są lepsze niż interfejsy znaczników?
źródło
Serializable
ponieważ adnotacja jest nonsensowna, a@NonNull
interfejs jest nonsensem. Powiedziałbym: adnotacje to znaczniki + metadane. BTW: Forefunner of Annotations był XDoclet, urodzony w Javadoc, zabity przez Annotations.Odpowiedzi:
writeObject(Serializable)
takiej, aby można było sprawdzić typ w czasie kompilacji - pozwala to uniknąć zanieczyszczania kodu nazwą interfejsu znacznika, gdyObject
potrzebny jest „zwykły ”. Na przykład, jeśli utworzysz klasę, która musi być serializowalna i ma składowe obiektów, będziesz zmuszony albo wykonać rzutowanie, albo utworzyć obiektySerializable
w czasie kompilacji. Jest to niewygodne, ponieważ interfejs pozbawiony jest jakiejkolwiek funkcjonalności.źródło
writeObject
jest prywatny, oznacza to, że na przykład klasa nie musi wywoływać implementacji superklasyCloneable
nie jest jasne, czy jest to biblioteka, czy obsługa JVM instancji, która została zmieniona.Nie można wymusić
Serializable
włączenia,writeObject
ponieważ elementy podrzędne klasy, której nie można serializować, mogą być serializowane, ale ich wystąpienia mogą zostać przeniesione z powrotem do klasy nadrzędnej. W rezultacie trzymanie odwołania do czegoś, co nie jest możliwe do serializacji (np.Object
), Nie oznacza, że odwołanej instancji nie można naprawdę serializować. Na przykład wKlasa nadrzędna (
Object
) nie jest możliwa do serializacji i została zainicjowana przy użyciu konstruktora bez parametrów. Wartość odwołujex
,String
jest możliwy do serializacji i instrukcja warunkowa będzie działać.źródło
Powyższe rozpoczęło się jako kopia posta na blogu, ale zostało lekko zredagowane pod kątem gramatyki.
źródło
Zrobiłem prostą demonstrację, aby rozwiązać wątpliwości nr 1 i 2:
Będziemy mieć Movable interfejs, który będzie implementowany przez
MobilePhone.java
Class i jeszcze jedną klasę,LandlinePhone.java
która NIE implementuje interfejsu MovableNasz interfejs znaczników:
LandLinePhone.java
iMobilePhone.java
Nasza niestandardowa klasa wyjątków: pakiet com;
Nasza klasa testowa:
TestMArkerInterface.java
Teraz, gdy wykonujemy główną klasę:
Więc która kiedykolwiek klasa implementuje interfejs znacznika
Movable
przejdzie test, w przeciwnym razie zostanie wyświetlony komunikat o błędzie.W ten sposób
instanceOf
wykonuje się sprawdzanie operatora dla serializowalnych , klonowalnych itpźródło
a / Interfejs znacznika, jak sugeruje jego nazwa, istnieje tylko po to, aby powiadomić wszystko, co o nim wie, że klasa coś deklaruje. Cokolwiek może być klasami JDK dla
Serializable
interfejsu lub dowolną klasą, którą napiszesz jako niestandardową.b / Jeśli jest to interfejs znacznika, nie powinno to implikować istnienia żadnej metody - lepiej byłoby uwzględnić metodę domniemaną w interfejsie. Można jednak zdecydować się go zaprojektować tak, jak chcesz, jeśli wiesz, dlaczego ty jej potrzebujesz
c / Istnieje niewielka różnica między pustym interfejsem a adnotacją, która nie używa wartości ani parametru. Ale różnica jest taka: adnotacja może zadeklarować listę kluczy / wartości, które będą dostępne w czasie wykonywania.
źródło
za. Zawsze postrzegałem je jako wzorzec projektowy i nic specjalnego dla JVM. Użyłem tego wzorca w kilku sytuacjach.
do. Wierzę, że używanie adnotacji do zaznaczania czegoś jest lepszym rozwiązaniem niż używanie interfejsów znaczników. Po prostu dlatego, że interfejsy są w pierwszej kolejności przeznaczone do definiowania wspólnych interfejsów typów / klas. Są częścią hierarchii klasowej.
Adnotacje mają na celu dostarczenie metainformacji do kodu i myślę, że znaczniki to metainformacje. Są więc dokładnie dla tego przypadku użycia.
źródło
Nie ma to nic wspólnego (koniecznie) z JVM i kompilatorami, ma coś wspólnego z każdym kodem, który jest zainteresowany i testuje interfejs danego znacznika.
To decyzja projektowa i nie bez powodu. Zobacz odpowiedź Audriusa Meškauskasa.
Jeśli chodzi o ten konkretny temat, nie sądzę, że jest to kwestia bycia lepszym lub gorszym. Interfejs znacznika robi dobrze, co powinien.
źródło
Głównym celem interfejsów znaczników jest tworzenie specjalnych typów, w których same typy nie mają własnego zachowania.
Tutaj save metoda zapewnia, że są zapisywane tylko obiekty klas implementujących interfejs MarkerEntity, dla innych typów InvalidEntityFoundException. Więc tutaj interfejs markera MarkerEntity definiuje typ, który dodaje specjalne zachowanie do klas implementujących go.
Chociaż adnotacje mogą teraz być również używane do oznaczania klas dla niektórych specjalnych zabiegów, ale adnotacje znaczników zastępują wzorzec nazewnictwa, a nie interfejsów Markera.
Ale adnotacje znaczników nie mogą w pełni zastąpić interfejsów znaczników, ponieważ; interfejsy znaczników służą do definiowania typu (jak już wyjaśniono powyżej), podczas gdy adnotacje znaczników nie.
Źródło komentarza interfejsu znacznika
źródło
Przede wszystkim argumentowałbym, że Serializable i Cloneable to złe przykłady interfejsów znaczników. Oczywiście, są to interfejsy z metodami, ale implikują metody, takie jak
writeObject(ObjectOutputStream)
. (Kompilator stworzywriteObject(ObjectOutputStream)
dla ciebie metodę, jeśli jej nie zastąpisz, a wszystkie obiekty już to zrobiłyclone()
, ale kompilator ponownie utworzyclone()
dla ciebie prawdziwą metodę, ale z zastrzeżeniami. Oba są dziwnymi skrajnymi przypadkami, które tak naprawdę nie są dobre przykłady projektów.)Interfejsy znaczników są zwykle używane do jednego z dwóch celów:
1) Jako skrót pozwalający uniknąć zbyt długiego tekstu, co może się zdarzyć w przypadku wielu rodzajów leków generycznych. Załóżmy na przykład, że masz ten podpis metody:
To niechlujne i irytujące pisanie, a co ważniejsze, trudne do zrozumienia. Zamiast tego rozważ to:
Wtedy twoja metoda wygląda następująco:
Nie tylko jest to bardziej przejrzyste, ale możesz teraz Javadoc interfejs Widget, a także łatwiej jest wyszukiwać wszystkie wystąpienia w kodzie Widgetu.
2) Interfejsy znaczników mogą być również używane jako sposób na obejście braku typów przecięć w Javie. W przypadku interfejsu znaczników można wymagać, aby coś było dwóch różnych typów, na przykład w sygnaturze metody. Załóżmy, że masz w swojej aplikacji jakiś widget interfejsu, taki jak opisaliśmy powyżej. Jeśli masz metodę, która wymaga Widżetu, który również pozwala na iterację (jest wymyślona, ale pracuj ze mną tutaj), jedynym dobrym rozwiązaniem jest utworzenie interfejsu znaczników, który rozszerza oba interfejsy:
A w swoim kodzie:
źródło
Serializable
lubCloneable
. Możesz to zweryfikować, przeglądając pliki swoich zajęć. Co więcej, tworzenie „skrótów interfejsów” nie jest tym, o co chodzi w interfejsach znaczników. Jest to naprawdę zła praktyka kodowania, ponieważ wymagałaby stworzenia dodatkowych klas implementacji , aby spełnić dodatkowe interfejsy. Nawiasem mówiąc, Java od dziesięciu lat ma typy przecięć. Dowiedz się więcej o rodzajach generycznych…Jeśli interfejs nie zawiera żadnej metody i poprzez implementację tego interfejsu, jeśli nasz obiekt uzyska jakąś zdolność, tego typu interfejsy nazywane są interfejsami znaczników.
źródło