Interfaces de marqueurs en Java?


134

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 objets de la classe implémentant cette interface doivent être traités d'une manière spéciale, comme la sérialisation, le clonage, etc.

Mais dernièrement, j'ai appris que cela n'avait en fait rien à voir avec le compilateur ou la JVM. Par exemple, dans le cas de l' Serializableinterface, la méthode writeObject(Object)de ObjectOutputStreamfait quelque chose comme instanceOf Serializablepour détecter si la classe implémente Serializable& lance en NotSerializableExceptionconsé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 marqueurs.

Maintenant mes doutes:

  1. La définition d'une interface de marqueur mentionnée ci-dessus au 1er point est-elle fausse? Comment définir alors une interface Marker?

  2. Et au lieu d'utiliser l' instanceOfopérateur, pourquoi la méthode ne peut-elle pas être quelque chose comme cela writeObject(Serializable)afin qu'il y ait une vérification de type au moment de la compilation plutôt que l'exécution?

  3. En quoi les annotations sont-elles meilleures que les interfaces de marqueurs?


8
Serializablecomme une annotation est un non-sens et @NonNullcomme une interface est un non-sens. Je dirais: les annotations sont des marqueurs + des métadonnées. BTW: Forefunner of Annotations était XDoclet, né à Javadoc, tué par des annotations.
Grim

Réponses:


117
  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) sa mise en œuvre implique un traitement spécial de la classe d'implémentation. La partie qui est incorrecte est que cela implique que JVM ou le compilateur traiterait différemment les objets de cette classe: vous avez raison de remarquer que c'est le code de la bibliothèque de classes Java qui traite ces objets 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 peut-elle pas être quelque chose comme cela writeObject(Serializable)pour qu'il y ait une vérification de type à la compilation - Cela vous permet d'éviter de polluer votre code avec le nom de l'interface du marqueur lorsqu'un "plain Object" est nécessaire. Par exemple, si vous créez une classe qui doit être sérialisable et qui a des membres d'objet, vous seriez obligé de faire un casting ou de créer vos objetsSerializable au moment de la compilation. Ceci est peu pratique, car l'interface est dépourvue de toute fonctionnalité.
  3. En quoi les annotations sont-elles meilleures que les interfaces de marqueurs? - Ils vous permettent d'atteindre le même objectif 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 «consomment».

14
La façon dont j'ai toujours compris c'est que les annotations sont une sorte d '' Interfaces de marqueurs 2.0 ': Serializable existe depuis Java 1.1, les annotations ont été ajoutées en 1.5
blagae

et parce que writeObjectc'est privé, cela signifie que par exemple une classe n'a pas besoin d'appeler l'implémentation de la superclasse
ratchet freak

15
Une chose à garder à l'esprit est que des annotations ont été ajoutées au langage quelques années après la conception initiale de la bibliothèque standard. Si les annotations avaient été dans la langue depuis le début, il est peu probable que Serializable aurait été une interface, cela aurait probablement été une annotation.
Theodore Norvell

Eh bien, dans ce cas, Cloneableil n'est pas clair s'il s'agit d'une bibliothèque ou d'un traitement JVM de l'instance qui est modifiée.
Holger

"[...] sans créer un type distinct pour cela." - Je dirais que c'est précisément ce qui les sépare: Une interface marqueur fait d' introduire un type, alors que les annotations (type de) does't.
aioobe

22

Il est impossible de faire respecter Serializablele writeObjectfait que les enfants de la classe non-sérialisable peuvent être sérialisable, mais leurs instances peut être de retour upcasted à la classe parente. Par conséquent, conserver une référence à quelque chose de non sérialisable (comme Object) ne signifie pas que l'instance référencée ne peut pas être vraiment sérialisée. Par exemple dans

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

la classe parent ( Object) n'est pas sérialisable et serait initialisée à l'aide de son constructeur sans paramètre. La valeur référencée par x, Stringest sérialisable et l'instruction conditionnelle courrait.


6

Une interface de marqueur en Java est une interface sans champs ni méthodes. En termes plus simples, une interface vide en Java est appelée interface de marqueur. Des exemples d'interfaces de marqueurs sont les interfaces Serializable, Cloneableet Remote. Celles-ci sont utilisées pour indiquer certaines informations aux compilateurs ou aux JVM. Donc, si la JVM voit qu'une classe est Serializable, elle peut effectuer une opération spéciale dessus. De même, si la machine virtuelle Java voit une classe s’implémenter Cloneable, elle peut effectuer certaines opérations pour prendre en charge le clonage. Il en va de même pour RMI et l' Remoteinterface. Bref, une interface de marqueur indique un signal ou une commande au compilateur ou à la JVM.

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.


6
Il est correct de copier mais veuillez également mentionner la source: javarevisited.blogspot.com/2012/01/… . Ce sera également bien si vous ne copiez pas les fautes d'orthographe. :)
Saurabh Patil

6

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 MobilePhone.javaClass et une autre classe LandlinePhone.javaqui n'implémentera 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;

public class NotMovableException extends Exception {

private static final long serialVersionUID = 1L;

    @Override
    public String 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(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");
}}

Maintenant, quand nous exécutons 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 implémente l'interface de marqueur Movable le test sera réussi, sinon le message d'erreur sera affiché.

C'est la façon dont la instanceOfvérification de l'opérateur est effectuée pour Serializable , Cloneable, etc.


5

a / Une interface de marqueur, comme son nom l'indique, n'existe que pour notifier tout ce qui en sait qu'une classe déclare quelque chose. Le tout peut être les classes JDK pour l' Serializableinterface, ou toute classe que vous écrivez vous-même pour une classe 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.


3

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

c. Je crois que l'utilisation d'annotations pour marquer quelque chose est une meilleure solution que l'utilisation d'interfaces de marqueurs. 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 les marqueurs sont des méta-informations. Donc, ils sont exactement pour ce cas d'utilisation.


3
  1. Cela n'a rien à voir (nécessairement) avec la JVM et les compilateurs, cela a quelque chose à voir avec n'importe quel code qui est intéressé et teste une interface de marqueur donnée.

  2. C'est une décision de conception et c'est fait 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 d’être meilleur ou pire. L'interface du marqueur fait ce qu'elle est censée faire très bien.


Pouvez-vous ajouter un lien vers "la réponse d'Audrius Meškauskas"? Je ne vois rien sur cette page avec ce nom.
Sarah Messer

2

L'objectif principal des interfaces de marqueur est de créer des types spéciaux où les types eux-mêmes n'ont aucun comportement propre.

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 objets des classes qui implémentent l'interface MarkerEntity sont enregistrés, pour les autres types InvalidEntityFoundException est levée. Donc, ici, l'interface de marqueur MarkerEntity définit un type qui ajoute un comportement spécial aux classes l'implémentant.

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

Mais les annotations de marqueur ne peuvent pas remplacer complètement les interfaces de marqueur car; Les interfaces de marqueur sont utilisées pour définir le type (comme déjà expliqué ci-dessus) alors que les annotations de marqueur ne le font pas.

Source du commentaire de l'interface du marqueur


1
+1 pour montrer qu'il est en fait utilisé comme un simple crochet de "sécurité" qu'un programmeur doit explicitement dire qu'il peut être sauvegardé pour l'exemple d'une base de données.
Ludvig W

1

Je dirais tout d'abord que Serializable et Cloneable sont de mauvais exemples d'interfaces de marqueurs. Bien sûr, ce sont des interfaces avec des méthodes, mais elles impliquent des méthodes, telles que writeObject(ObjectOutputStream). (Le compilateur créera une writeObject(ObjectOutputStream)méthode pour vous si vous ne la remplacez pas, et tous les objets l'ont déjà fait clone(), mais le compilateur créera à nouveau une clone()méthode réelle pour vous, mais avec des mises en garde. Ces deux cas sont étranges qui ne le sont pas vraiment bons exemples de conception.)

Les interfaces de marqueurs sont généralement utilisées à deux fins:

1) En tant que 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<String, Map<String, SomethingElse<Integer, Long>>>) { ... }

C'est compliqué et ennuyeux à taper, et plus important encore, difficile à comprendre. Considérez plutôt ceci:

public interface Widget extends Foobar<String, Map<String, SomethingElse<Integer, Long>>> { }

Ensuite, votre méthode ressemble à ceci:

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

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

2) Les interfaces de marqueurs 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, comme dans une signature de méthode. Supposons que vous ayez un widget d'interface 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 l'itérer (c'est artificiel, mais travaillez 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<String>, Widget { }

Et dans votre code:

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

1
Le compilateur ne pas créer des méthodes si vos outils de classe Serializableou Cloneable. Vous pouvez le vérifier en inspectant vos fichiers de classe. De plus, la création «d'interfaces de raccourcis» n'est pas l' objectif des interfaces de marqueur. Et c'est une très mauvaise pratique de codage car elle nécessiterait la création de classes d' implémentation supplémentaires pour remplir les interfaces supplémentaires. À propos, Java a des types d'intersection depuis une décennie maintenant. En savoir plus sur les génériques…
Holger

Pour clonable, vous avez raison. Cela change ce que fait Object.clone (). C'est toujours horrible. Voir le lien Josh Bloch Les interfaces de raccourci ne sont pas nécessairement un bon modèle de conception, car vous restreignez arbitrairement ce que vous pouvez envoyer à une méthode. Dans le même temps, j'estime qu'écrire du code plus clair, quoique légèrement plus restrictif, est généralement un compromis raisonnable. Quant aux types d'intersection, cela ne s'applique qu'aux génériques, et sont non dénotables, et donc inutiles dans de nombreuses occasions. Essayez d'avoir une variable d'instance qui est à la fois sérialisable et, bien, autre chose.
MikeyB

0

Si une interface ne contient aucune méthode et en implémentant cette interface, si notre objet obtient une certaine capacité, ce type d'interface est appelé interfaces de marqueur.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.