Interfaces de marqueurs en Java

1. Introduction

Dans ce rapide didacticiel, nous découvrirons les interfaces de marqueurs en Java.

2. Interfaces de marqueurs

Une interface de marqueur est une interface qui ne contient ni méthodes ni constantes . Il fournit des informations de type à l'exécution sur les objets , de sorte que le compilateur et la JVM disposent d' informations supplémentaires sur l'objet .

Une interface de marqueur est également appelée interface de balisage.

Bien que les interfaces de marqueurs soient toujours utilisées, elles indiquent très probablement une odeur de code et doivent être utilisées avec précaution. La raison principale en est qu'ils brouillent les lignes sur ce que représente une interface puisque les marqueurs ne définissent aucun comportement. Un développement plus récent favorise les annotations pour résoudre certains des mêmes problèmes.

3. Interfaces de marqueurs JDK

Java possède de nombreuses interfaces de marqueurs intégrées, telles que Serializable , Cloneable et Remote.

Prenons l'exemple de l' interface clonable . Si nous essayons de cloner un objet qui n'implémente pas cette interface, la machine virtuelle Java lève une CloneNotSupportedException . Par conséquent, l' interface de marqueur clonable est un indicateur pour la JVM que nous pouvons appeler la méthode Object.clone () .

De la même manière, lors de l'appel de la méthode ObjectOutputStream.writeObject () , la JVM vérifie si l'objet implémente l' interface de marqueur Serializable . Quand ce n'est pas le cas, une NotSerializableException est levée. Par conséquent, l'objet n'est pas sérialisé dans le flux de sortie.

4. Interface de marqueur personnalisé

Créons notre propre interface de marqueurs.

Par exemple, nous pourrions créer un marqueur qui indique si un objet peut être supprimé de la base de données:

public interface Deletable { }

Afin de supprimer une entité de la base de données, l'objet représentant cette entité doit implémenter notre interface de marqueurs supprimables :

public class Entity implements Deletable { // implementation details }

Disons que nous avons un objet DAO avec une méthode pour supprimer des entités de la base de données. Nous pouvons écrire notre méthode delete () pour que seuls les objets implémentant notre interface de marqueurs puissent être supprimés:

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

Comme nous pouvons le voir, nous donnons une indication à la JVM, sur le comportement d'exécution de nos objets. Si l'objet implémente notre interface de marqueur, il peut être supprimé de la base de données.

5. Interfaces de marqueurs et annotations

En introduisant des annotations, Java nous a fourni une alternative pour obtenir les mêmes résultats que les interfaces de marqueurs. De plus, comme les interfaces de marqueurs, nous pouvons appliquer des annotations à n'importe quelle classe, et nous pouvons les utiliser comme indicateurs pour effectuer certaines actions.

Alors, quelle est la principale différence?

Contrairement aux annotations, les interfaces nous permettent de tirer parti du polymorphisme . En conséquence, nous pouvons ajouter des restrictions supplémentaires à l'interface des marqueurs.

Par exemple, ajoutons une restriction selon laquelle seul un type de forme peut être supprimé de la base de données:

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

Dans ce cas, notre interface de marqueur, appelons-la DeletableShape, ressemblera à ceci:

public interface DeletableShape extends Shape { }

Ensuite, notre classe implémentera l'interface des marqueurs:

public class Rectangle implements DeletableShape { // implementation details }

Par conséquent, toutes les implémentations DeletableShape sont également des implémentations Shape . Évidemment, nous ne pouvons pas faire cela en utilisant des annotations .

Cependant, chaque décision de conception a des compromis et le polymorphisme peut être utilisé comme contre-argument contre les interfaces de marqueurs. Dans notre exemple, chaque classe étendant Rectangle implémentera automatiquement DeletableShape.

6. Interfaces de marqueurs et interfaces typiques

Dans l'exemple précédent, nous pourrions obtenir les mêmes résultats en modifiant la méthode delete () de notre DAO pour tester si notre objet est un Shape ou non , au lieu de tester s'il s'agit d'un Deletable:

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

Alors pourquoi créer une interface de marqueurs alors que nous pouvons obtenir les mêmes résultats en utilisant une interface typique?

Imaginons qu'en plus du type Shape , nous souhaitons également supprimer le type Person de la base de données. Dans ce cas, il existe deux options pour y parvenir:

La première option consiste à ajouter une vérification supplémentaire à notre précédente méthode delete () pour vérifier si l'objet à supprimer est une instance de Person ou non.

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

Mais que se passe-t-il si nous voulons également supprimer d'autres types de la base de données? Évidemment, ce ne sera pas une bonne option car nous devons changer notre méthode pour chaque nouveau type .

La deuxième option consiste à faire en sorte que le type Person implémente l' interface Shape , qui agit comme une interface de marqueur. Mais un objet Person est-il vraiment une forme ? La réponse est clairement non, et cela rend la deuxième option pire que la première.

Par conséquent, bien que nous puissions obtenir les mêmes résultats en utilisant une interface typique comme marqueur, nous nous retrouverons avec une mauvaise conception.

7. Conclusion

Dans cet article, nous avons discuté de ce que sont les interfaces de marqueur et comment elles peuvent être utilisées. Ensuite, nous avons examiné quelques exemples Java intégrés de ce type d'interfaces et comment elles sont utilisées par le JDK.

Ensuite, nous avons créé notre propre interface de marqueur et l'avons comparée à l'aide d'une annotation. Enfin, nous finissons par voir pourquoi c'est une bonne pratique d'utiliser une interface de marqueur dans certains scénarios au lieu d'une interface traditionnelle.

Comme toujours, le code est disponible sur GitHub.