Interfacce Marker in Java?

Mi è stato insegnato che l’interfaccia Marker in Java è un’interfaccia vuota e viene utilizzata per segnalare al compilatore o alla JVM che gli oggetti della class che implementano questa interfaccia devono essere trattati in modo speciale, come la serializzazione, la clonazione, ecc.

Ma ultimamente ho imparato che in realtà non ha nulla a che fare con il compilatore o la JVM. Ad esempio, in caso di interfaccia Serializable il metodo writeObject(Object) di ObjectOutputStream fa qualcosa di simile a instanceOf Serializable per rilevare se la class implementa Serializable & getta NotSerializableException conseguenza. Tutto è gestito nel codice e questo sembra essere un modello di progettazione, quindi penso che possiamo definire le nostre interfacce marker.

Ora i miei dubbi:

  1. La definizione di un’interfaccia marcatore sopra menzionata nel primo punto è errata? Come possiamo definire un’interfaccia Marker allora?

  2. E invece di usare l’operatore instanceOf perché il metodo non può essere qualcosa come writeObject(Serializable) modo che ci sia un controllo del tipo in fase di compilazione piuttosto che in runtime?

  3. In che modo le annotazioni sono migliori delle Marker Interfaces?

  1. La definizione di un’interfaccia marker menzionata sopra nel primo punto è errata? – È corretto nelle parti che (1) un’interfaccia marker deve essere vuota, e (2) implementarla intende implicare un trattamento speciale della class di implementazione. La parte che non è corretta è che implica che JVM o il compilatore tratterà gli oggetti di quella class in modo diverso: è corretto osservare che è il codice della libreria di classi Java che tratta questi oggetti come clonabili, serializzabili, ecc. Ha niente a che fare con il compilatore o la JVM.
  2. invece di usare l’operatore instanceOf perché il metodo non può essere qualcosa come writeObject(Serializable) modo che ci sia un controllo del tipo in fase di compilazione – Questo ti permette di evitare di inquinare il tuo codice con il nome dell’interfaccia marcatore quando un ” Object semplice” è necessario. Ad esempio, se crei una class che deve essere serializzabile e ha membri di oggetti, sarai costretta a eseguire il cast o rendere i tuoi oggetti Serializable in fase di compilazione. Ciò è inopportuno, poiché l’interfaccia è priva di qualsiasi funzionalità.
  3. In che modo le annotazioni sono migliori di Marker Interfaces? – Ti permettono di raggiungere lo stesso scopo di trasmettere metadati sulla class ai suoi consumatori senza creare un tipo separato per esso. Le annotazioni sono anche più potenti, lasciando che i programmatori trasmettano informazioni più sofisticate alle classi che “lo consumano”.

Non è ansible applicare Serializable su writeObject perché i figli di una class non serializzabile possono essere serializzabili, ma possono essere ricondotti alla class genitore. Di conseguenza, mantenere un riferimento a qualcosa non serializzabile (come Object ) non significa che l’istanza indicata non possa essere serializzata. Ad esempio in

  Object x = "abc"; if (x instanceof Serializable) { } 

la class genitore ( Object ) non è serializzabile e verrebbe inizializzata usando il suo costruttore senza parametri. Il valore referenziato da x , String , è serializzabile e l’istruzione condizionale verrebbe eseguita.

Un’interfaccia marker in Java è un’interfaccia senza campi o metodi. In parole povere, un’interfaccia vuota in Java è chiamata interfaccia marker. Esempi di interfacce marker sono le interfacce Serializable , Cloneable e Remote . Questi sono usati per indicare alcune informazioni a compilatori o JVM. Quindi se la JVM vede che una class è Serializable , può fare qualche operazione speciale su di essa. Allo stesso modo, se la JVM vede che alcune classi implementano Cloneable , può eseguire alcune operazioni per supportare la clonazione. Lo stesso vale per RMI e l’interfaccia Remote . Quindi, in breve, un’interfaccia marker indica un segnale o un comando al compilatore o JVM.

Quanto sopra è iniziato come una copia di un post sul blog ma è stato leggermente modificato per la grammatica.

un’interfaccia marker A / A come suggerisce il nome, esiste solo per notificare tutto ciò che sa che una class dichiara qualcosa. Tutto ciò che può essere le classi JDK per l’interfaccia Serializable , o qualsiasi class si scrive yoursel per uno personalizzato.

b / Se è un’interfaccia marcatore, non dovrebbe implicare l’esistenza di alcun metodo – sarebbe meglio includere il metodo implicito nell’interfaccia. Ma puoi decidere di progettarlo come vuoi se sai perché ne hai bisogno

c / C’è poca differenza tra un’interfaccia vuota e un’annotazione che non usa alcun valore o parametro. Ma la differenza c’è: un’annotazione può dichiarare un elenco di chiavi / valori che saranno accessibili in fase di esecuzione.

un. Li ho sempre visti come un motivo di progettazione e niente JVM-Special ho usato quel modello in diverse situazioni.

c. Credo che usare Annotazioni per contrassegnare qualcosa sia una soluzione migliore quindi utilizzare le interfacce marcatore. Semplicemente perché le interfacce sono in primo luogo volte a definire interfacce comuni di tipi / classi. Fanno parte della gerarchia di class.

Le annotazioni hanno lo scopo di fornire meta-informazioni al codice, e penso che i marcatori siano meta-informazioni. Quindi sono esattamente per quel caso d’uso.

  1. Non ha nulla a che fare (necessariamente) con la JVM e con i compilatori, ha qualcosa a che fare con qualsiasi codice che sia interessato e sta testando una determinata interfaccia marker.

  2. È una decisione di progettazione ed è fatta per una buona ragione. Vedi la risposta di Audrius Meškauskas.

  3. Per quanto riguarda questo particolare argomento, non penso che sia una questione di essere meglio o peggio. L’interfaccia del marcatore sta facendo esattamente quello che dovrebbe fare.

Lo scopo principale delle interfacce marker è di creare tipi speciali in cui i tipi stessi non hanno un loro proprio comportamento.

 public interface MarkerEntity { } public boolean save(Object object) throws InvalidEntityFoundException { if(!(object instanceof MarkerEntity)) { throw new InvalidEntityFoundException("Invalid Entity Found, can't be saved); } return db.save(object); } 

Qui il metodo save assicura che solo gli oggetti delle classi che implementano l’interfaccia MarkerEntity vengano salvati, per gli altri tipi viene generata l’eccezione InvalidEntityFoundException. Quindi, qui l’interfaccia MarkerEntity marker sta definendo un tipo che aggiunge un comportamento speciale alle classi che lo implementano.

Sebbene le annotazioni possano essere utilizzate anche ora per contrassegnare le classi per alcuni trattamenti speciali, le annotazioni dei marcatori sono sostituzioni per il modello di denominazione e non per le interfacce Marker.

Ma le annotazioni dei marker non possono sostituire completamente le interfacce marker perché; le interfacce marcatore sono usate per definire il tipo (come già spiegato sopra) dove le annotazioni marcatore no.

Fonte per il commento all’interfaccia marcatore

Direi per prima cosa che Serializable e Cloneable sono cattivi esempi di interfacce marker. Certo, sono interfacce con i metodi, ma implicano metodi, come writeObject(ObjectOutputStream) . (Il compilatore creerà per te un metodo writeObject(ObjectOutputStream) se non lo sovrascrivi, e tutti gli oggetti hanno già clone() , ma il compilatore creerà di nuovo un vero metodo clone() per te ma con avvertimenti. questi sono casi di bordo strani che in realtà non sono buoni esempi di design.)

Le interfacce marcatore sono generalmente utilizzate per uno dei due scopi:

1) Come scorciatoia per evitare un tipo eccessivamente lungo, che può accadere con un sacco di generici. Ad esempio, supponiamo tu abbia questa firma del metodo:

 public void doSomething(Foobar>>) { ... } 

È complicato e fastidioso digitare e, cosa ancora più importante, difficile da capire. Considera questo invece:

 public interface Widget extends Foobar>> { } 

Quindi il tuo metodo si presenta così:

 public void doSomething(Widget widget) { ... } 

Non solo è più chiaro, ma ora puoi Javadoc l’interfaccia Widget, ed è anche più facile cercare tutte le occorrenze nel tuo codice di Widget.

2) Le interfacce Marker possono anche essere utilizzate come un modo per aggirare la mancanza di tipi di intersezione di Java. Con un’interfaccia marcatore, puoi richiedere che ci sia qualcosa di due tipi diversi, come nella firma di un metodo. Supponiamo che tu abbia un widget di interfaccia nella tua applicazione, come descritto sopra. Se hai un metodo che richiede un Widget che ti capita di farlo scorrere (è inventato, ma lavora con me qui), la tua unica buona soluzione è creare un’interfaccia marker che estenda entrambe le interfacce:

 public interface IterableWidget extends Iterable, Widget { } 

E nel tuo codice:

 public void doSomething(IterableWidget widget) { for (String s : widget) { ... } } 

Se un’interfaccia non contiene alcun metodo e implora quell’interfaccia se il nostro object otterrà una certa capacità, tali tipi di interfacce verranno chiamate marker itarfaces.

Ho fatto una semplice dimostrazione per risolvere i dubbi n. 1 e 2:

Avremo un’interfaccia mobile che sarà implementata da MobilePhone.java Class e un’altra class LandlinePhone.java che NON implementano l’interfaccia mobile

La nostra interfaccia marker:

 package com; public interface Movable { } 

LandLinePhone.java e MobilePhone.java

  package com; class LandLinePhone { // more code here } class MobilePhone implements Movable { // more code here } 

La nostra class di eccezioni personalizzate: pacchetto com;

la class pubblica NotMovableException estende l’eccezione {

 private static final long serialVersionUID = 1L; @Override public String getMessage() { return "this object is not movable"; } // more code here } 

La nostra class di test: TestMArkerInterface.java

 package com; public class TestMarkerInterface { public static void main(String[] args) throws NotMovableException { MobilePhone mobilePhone = new MobilePhone(); LandLinePhone landLinePhone = new LandLinePhone(); TestMarkerInterface.goTravel(mobilePhone); TestMarkerInterface.goTravel(landLinePhone); } public static void goTravel(Object o) throws NotMovableException { if (!(o instanceof Movable)) { System.out.println("you cannot use :" + o.getClass().getName() + " while travelling"); throw new NotMovableException(); } System.out.println("you can use :" + o.getClass().getName() + " while travelling"); }} 

Ora quando eseguiamo la class principale:

 you can use :com.MobilePhone while travelling you cannot use :com.LandLinePhone while travelling Exception in thread "main" com.NotMovableException: this object is not movable at com.TestMarkerInterface.goTravel(TestMarkerInterface.java:22) at com.TestMarkerInterface.main(TestMarkerInterface.java:14) 

Quindi, quale class implementa l’interfaccia marker mobile, passerà il test altrimenti verrà visualizzato un messaggio di errore.

Questo è il modo in cui viene eseguito il controllo dell’istanzaOf per Serializable, Cloneable ecc