Marker Interfaces in Java

1. Introduzione

In questo rapido tutorial, impareremo a conoscere le interfacce dei marker in Java.

2. Marker Interfaces

Un'interfaccia marker è un'interfaccia che non ha metodi o costanti al suo interno . Fornisce informazioni sul tipo di runtime sugli oggetti , quindi il compilatore e JVM dispongono di ulteriori informazioni sull'oggetto .

Un'interfaccia marker è anche chiamata interfaccia di tagging.

Sebbene le interfacce marker siano ancora in uso, molto probabilmente indicano un odore di codice e dovrebbero essere utilizzate con attenzione. La ragione principale di ciò è che sfocano le linee su ciò che rappresenta un'interfaccia poiché i marcatori non definiscono alcun comportamento. Lo sviluppo più recente favorisce le annotazioni per risolvere alcuni degli stessi problemi.

3. Interfacce Marker JDK

Java ha molte interfacce marker integrate, come Serializable , Cloneable e Remote.

Prendiamo l'esempio dell'interfaccia Clonabile . Se proviamo a clonare un oggetto che non implementa questa interfaccia, la JVM genera un'eccezione CloneNotSupportedException . Quindi, l' interfaccia del marker clonabile è un indicatore della JVM che possiamo chiamare il metodo Object.clone () .

Allo stesso modo, quando si chiama il metodo ObjectOutputStream.writeObject () , la JVM controlla se l'oggetto implementa l' interfaccia del marker Serializable . Quando non è il caso, viene generata un'eccezione NotSerializableException . Pertanto, l'oggetto non viene serializzato nel flusso di output.

4. Interfaccia Marker personalizzata

Creiamo la nostra interfaccia marker.

Ad esempio, potremmo creare un marker che indica se un oggetto può essere rimosso dal database:

public interface Deletable { }

Per eliminare un'entità dal database, l'oggetto che rappresenta questa entità deve implementare la nostra interfaccia Deletable marker:

public class Entity implements Deletable { // implementation details }

Supponiamo di avere un oggetto DAO con un metodo per rimuovere entità dal database. Possiamo scrivere il nostro metodo delete () in modo che solo gli oggetti che implementano la nostra interfaccia marker possano essere cancellati:

public class ShapeDao { // other dao methods public boolean delete(Object object) { if (!(object instanceof Deletable)) { return false; } // delete implementation details return true; } }

Come possiamo vedere, stiamo dando un'indicazione alla JVM, sul comportamento di runtime dei nostri oggetti. Se l'oggetto implementa la nostra interfaccia marker, può essere cancellato dal database.

5. Marker Interfaces vs. Annotazioni

Introducendo le annotazioni, Java ci ha fornito un'alternativa per ottenere gli stessi risultati delle interfacce dei marker. Inoltre, come le interfacce marker, possiamo applicare annotazioni a qualsiasi classe e possiamo usarle come indicatori per eseguire determinate azioni.

Allora qual è la differenza fondamentale?

A differenza delle annotazioni, le interfacce ci consentono di sfruttare il polimorfismo . Di conseguenza, possiamo aggiungere ulteriori restrizioni all'interfaccia del marker.

Ad esempio, aggiungiamo una restrizione in base alla quale solo un tipo di forma può essere rimosso dal database:

public interface Shape { double getArea(); double getCircumference(); }

In questo caso, la nostra interfaccia marker, chiamiamola DeletableShape, sarà simile alla seguente:

public interface DeletableShape extends Shape { }

Quindi la nostra classe implementerà l'interfaccia marker:

public class Rectangle implements DeletableShape { // implementation details }

Pertanto, tutte le implementazioni di DeletableShape sono anche implementazioni di Shape . Ovviamente, non possiamo farlo usando le annotazioni .

Tuttavia, ogni decisione di progettazione ha dei compromessi e il polimorfismo può essere utilizzato come controargomentazione contro le interfacce dei marker. Nel nostro esempio, ogni classe che estende Rectangle implementerà automaticamente DeletableShape.

6. Marker Interfaces vs. Tipiche Interfacce

Nell'esempio precedente, potremmo ottenere gli stessi risultati modificando il metodo delete () del nostro DAO per verificare se il nostro oggetto è uno Shape o meno , invece di verificare se è un Deletable:

public class ShapeDao { // other dao methods public boolean delete(Object object) { if (!(object instanceof Shape)) { return false; } // delete implementation details return true; } }

Allora perché creare un'interfaccia marker quando possiamo ottenere gli stessi risultati utilizzando un'interfaccia tipica?

Immaginiamo che, oltre al tipo Shape , vogliamo rimuovere anche il tipo Person dal database. In questo caso, ci sono due opzioni per ottenerlo:

La prima opzione è aggiungere un ulteriore controllo al nostro precedente metodo delete () per verificare se l'oggetto da eliminare è un'istanza di Person o meno.

public boolean delete(Object object) { if (!(object instanceof Shape || object instanceof Person)) { return false; } // delete implementation details return true; }

Ma cosa succede se abbiamo più tipi che vogliamo rimuovere anche dal database? Ovviamente, questa non sarà una buona opzione perché dobbiamo cambiare il nostro metodo per ogni nuovo tipo .

La seconda opzione è fare in modo che il tipo Person implementi l' interfaccia Shape , che funge da interfaccia marker. Ma un oggetto Person è davvero una Forma ? La risposta è chiaramente no e questo rende la seconda opzione peggiore della prima.

Quindi, sebbene possiamo ottenere gli stessi risultati utilizzando un'interfaccia tipica come indicatore, ci ritroveremo con un design scadente.

7. Conclusione

In questo articolo, abbiamo discusso cosa sono le interfacce marker e come possono essere utilizzate. Quindi abbiamo esaminato alcuni esempi Java incorporati di questo tipo di interfacce e come vengono utilizzate dal JDK.

Successivamente, abbiamo creato la nostra interfaccia marker e l'abbiamo valutata rispetto all'utilizzo di un'annotazione. Infine, finiamo per capire perché è una buona pratica utilizzare un'interfaccia marker in alcuni scenari invece di un'interfaccia tradizionale.

Come sempre, il codice può essere trovato su GitHub.