Interfaces de marqueurs en Java?

On m’a appris que l’interface Marker en Java est une interface vide et est utilisée pour signaler au compilateur ou à la JVM que les objects de la classe implémentant cette interface doivent être traités de manière particulière, comme la sérialisation, le clonage, etc.

Mais récemment, j’ai appris que cela n’avait rien à voir avec le compilateur ou la JVM. Par exemple, dans le cas d’une interface Serializable , la méthode writeObject(Object) de ObjectOutputStream fait quelque chose comme instanceOf Serializable pour détecter si la classe implémente Serializable & lance NotSerializableException conséquence. Tout est géré dans le code et cela semble être un modèle de conception, donc je pense que nous pouvons définir nos propres interfaces de marqueur.

Maintenant mes doutes:

  1. La définition d’une interface de marqueur mentionnée ci-dessus au 1er point est-elle erronée? Comment pouvons-nous définir une interface de marqueur alors?

  2. Et au lieu d’utiliser l’opérateur instanceOf , pourquoi la méthode ne peut-elle pas ressembler à writeObject(Serializable) sorte qu’il existe une vérification de type à la compilation plutôt qu’à l’exécution?

  3. Comment les annotations sont-elles meilleures que les interfaces de marqueur?

  1. La définition d’une interface de marqueur mentionnée ci-dessus au 1er point est-elle erronée? – Il est correct dans les parties que (1) une interface de marqueur doit être vide, et (2) l’implémenter est censé impliquer un traitement particulier de la classe d’implémentation. La partie incorrecte est que cela implique que JVM ou le compilateur traiterait les objects de cette classe différemment: vous avez raison d’observer que c’est le code de la bibliothèque de classes Java qui traite ces objects comme clonables, sérialisables, etc. rien à voir avec le compilateur ou la JVM.
  2. au lieu d’utiliser l’opérateur instanceOf, pourquoi la méthode ne ressemble- writeObject(Serializable) pas à writeObject(Serializable) pour qu’il y ait une vérification de type à la compilation ? Cela vous évite de polluer votre code avec le nom de l’interface marqueur ” Object ” est nécessaire. Par exemple, si vous créez une classe devant être sérialisable et que vous avez des membres d’object, vous serez obligé soit de faire des transtypages, soit de rendre vos objects Serializable au moment de la compilation. Ceci est gênant, car l’interface est dépourvue de toute fonctionnalité.
  3. Comment les annotations sont-elles meilleures que les interfaces de marqueur? – Ils vous permettent d’atteindre le même objective de transmettre des métadonnées sur la classe à ses consommateurs sans créer un type distinct pour elle. Les annotations sont également plus puissantes, permettant aux programmeurs de transmettre des informations plus sophistiquées aux classes qui les “consumnt”.

Il n’est pas possible d’imposer Serializable sur writeObject car les enfants d’une classe non sérialisable peuvent être sérialisables, mais ces instances peuvent être renvoyées vers la classe parente. Par conséquent, détenir une référence à quelque chose de non sérialisable (comme Object ) ne signifie pas que l’instance référencée ne peut vraiment pas être sérialisée. Par exemple dans

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

la classe parente ( Object ) n’est pas sérialisable et serait initialisée en utilisant son constructeur sans paramètre. La valeur référencée par x , Ssortingng , est sérialisable et l’instruction conditionnelle est exécutée.

Une interface de marqueur en Java est une interface sans champs ni méthodes. Plus simplement, une interface vide en Java s’appelle une interface de marqueur. Les interfaces Serializable , Cloneable et Remote sont des exemples d’interfaces de marqueur. Ceux-ci sont utilisés pour indiquer certaines informations aux compilateurs ou aux machines virtuelles Java. Donc, si la JVM voit qu’une classe est Serializable , elle peut y effectuer des opérations spéciales. De même, si la JVM voit une classe implémentant Cloneable , elle peut effectuer certaines opérations pour prendre en charge le clonage. La même chose est vraie pour RMI et l’interface Remote . En bref, une interface de marqueur indique un signal ou une commande au compilateur ou à la machine virtuelle Java.

Ce qui précède a commencé comme une copie d’ un article de blog mais a été légèrement modifié pour la grammaire.

a / Une interface de marqueur comme son nom l’indique n’existe que pour notifier quelque chose qui en sait qu’une classe déclare quelque chose. Le tout peut être les classes JDK pour l’interface Serializable , ou n’importe quelle classe que vous écrivez pour une interface personnalisée.

b / S’il s’agit d’une interface de marqueur, cela ne devrait impliquer l’existence d’aucune méthode – il serait préférable d’inclure la méthode implicite dans l’interface. Mais vous pouvez décider de le concevoir comme vous le souhaitez si vous savez pourquoi vous en avez besoin

c / Il y a peu de différence entre une interface vide et une annotation qui n’utilise aucune valeur ou paramètre. Mais la différence est là: une annotation peut déclarer une liste de clés / valeurs qui seront accessibles au moment de l’exécution.

une. Je les ai toujours vus comme un motif de conception et rien de spécial JVM-J’ai utilisé ce modèle dans plusieurs situations.

c. Je crois que l’utilisation des annotations pour marquer quelque chose est une meilleure solution que d’utiliser des interfaces de marqueur. Tout simplement parce que les interfaces visent en premier lieu à définir des interfaces communes de types / classes. Ils font partie de la hiérarchie des classes.

Les annotations visent à fournir des méta-informations au code, et je pense que ces marqueurs sont des méta-informations. Donc, ils sont exactement pour ce cas d’utilisation.

  1. Cela n’a rien à voir (nécessairement) avec la JVM et les compilateurs, cela a quelque chose à voir avec tout code qui intéresse et qui teste une interface de marqueur donnée.

  2. C’est une décision de conception et c’est pour une bonne raison. Voir la réponse d’Audrius Meškauskas.

  3. En ce qui concerne ce sujet particulier, je ne pense pas que ce soit une question de mieux ou de pire. L’interface marqueur fait ce qu’elle est censée faire.

L’objective principal des interfaces de marqueurs est de créer des types spéciaux où les types eux-mêmes n’ont aucun comportement.

 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); } 

Ici, la méthode save garantit que seuls les objects des classes implémentant l’interface MarkerEntity sont enregistrés, pour les autres types, InvalidEntityFoundException est levé. Donc, ici, l’interface de marqueur MarkerEntity définit un type qui ajoute un comportement spécial aux classes qui l’implémentent.

Bien que les annotations puissent également être utilisées maintenant pour marquer des classes pour certains traitements spéciaux, les annotations de marqueurs remplacent le modèle de dénomination, pas pour les interfaces de marqueurs.

Mais les annotations des marqueurs ne peuvent pas entièrement remplacer les interfaces des marqueurs car; Les interfaces de marqueurs sont utilisées pour définir le type (comme expliqué ci-dessus), contrairement aux annotations de marqueur.

Source pour le commentaire d’interface de marqueur

Je dirais d’abord que Serializable et Cloneable sont de mauvais exemples d’interfaces de marqueur. Bien sûr, ils sont des interfaces avec des méthodes, mais ils impliquent des méthodes, telles que writeObject(ObjectOutputStream) . (Le compilateur créera pour vous une méthode writeObject(ObjectOutputStream) si vous ne la remplacez pas, et tous les objects ont déjà clone() , mais le compilateur créera à nouveau une méthode clone() avec des réserves. Ce sont des cas bizarres qui ne sont pas de bons exemples de conception.)

Les interfaces de marqueur sont généralement utilisées pour deux raisons:

1) Comme raccourci pour éviter un type trop long, ce qui peut arriver avec beaucoup de génériques. Par exemple, disons que vous avez cette signature de méthode:

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

C’est désordonné et ennuyeux à taper, et surtout, difficile à comprendre. Considérez ceci à la place:

 public interface Widget extends Foobar>> { } 

Ensuite, votre méthode ressemble à ceci:

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

Non seulement est-il plus clair, mais vous pouvez maintenant utiliser l’interface Widget Javadoc, et il est également plus facile de rechercher toutes les occurrences dans votre code de Widget.

2) Les interfaces de marqueur peuvent également être utilisées pour contourner le manque de types d’intersection de Java. Avec une interface de marqueur, vous pouvez exiger que quelque chose soit de deux types différents, par exemple dans une signature de méthode. Disons que vous avez une interface Widget dans votre application, comme nous l’avons décrit ci-dessus. Si vous avez une méthode qui nécessite un Widget qui vous permet également de parcourir ce dernier (il est artificiel, mais fonctionne avec moi ici), votre seule bonne solution est de créer une interface de marqueur qui étend les deux interfaces:

 public interface IterableWidget extends Iterable, Widget { } 

Et dans votre code:

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

Si une interface ne contient aucune méthode et en implémentant cette interface si notre object a une certaine capacité, ce type d’interface est appelé une itarface de marqueur.

J’ai fait une démonstration simple pour résoudre les doutes n ° 1 et 2:

Nous aurons une interface Movable qui sera implémentée par la classe MobilePhone.java et une autre classe LandlinePhone.java qui n’implémente PAS l’ interface Movable

Notre interface de marqueur:

 package com; public interface Movable { } 

LandLinePhone.java et MobilePhone.java

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

Notre classe d’exception personnalisée: package com;

la classe publique NotMovableException étend Exception {

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

Notre classe de test: TestMArkerInterface.java

 package com; public class TestMarkerInterface { public static void main(Ssortingng[] 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"); }} 

Maintenant, quand on exécute la classe 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) 

Donc, quelle que soit la classe qui implémente l’interface de marqueur, Movable passera le test sinon le message d’erreur sera affiché.

Ceci est la façon dont la vérification des opérateurs instanceOf est effectuée pour Serializable, Cloneable etc