Interfaces de marcadores em Java?

134

Eu estava aprendendo que a interface Marker em Java é uma interface vazia e é usada para sinalizar ao compilador ou JVM que os objetos da classe que implementam essa interface devem ser tratados de uma maneira especial, como serialização, clonagem etc.

Mas ultimamente eu aprendi que ele realmente não tem nada a ver com o compilador ou a JVM. Por exemplo, no caso Serializableda interface do método writeObject(Object)de ObjectOutputStreamfaz algo parecido instanceOf Serializablepara detectar se os implementos classe Serializablee mantas NotSerializableExceptionem conformidade. Tudo é tratado no código e isso parece ser um padrão de design, então acho que podemos definir nossas próprias interfaces de marcador.

Agora minhas dúvidas:

  1. A definição de uma interface de marcador mencionada acima no 1º ponto está errada? Como podemos definir uma interface de marcador então?

  2. E, em vez de usar o instanceOfoperador, por que o método não pode ser algo parecido writeObject(Serializable)para que haja uma verificação do tipo em tempo de compilação em vez do tempo de execução?

  3. Como as anotações são melhores que as interfaces de marcador?

Xavier DSouza
fonte
8
Serializablecomo uma anotação não faz sentido e @NonNullcomo uma interface não faz sentido. Eu diria: Anotações são marcadores + metadados. BTW: O Forefunner of Annotations foi o XDoclet, nascido em Javadoc, morto por Annotations.
Grim

Respostas:

117
  1. A definição de uma interface de marcador mencionada acima no 1º ponto está errada? - Está correto nas partes que (1) uma interface de marcador deve estar vazia e (2) implementá-la deve implicar algum tratamento especial da classe de implementação. A parte incorreta é que isso implica que a JVM ou o compilador trataria os objetos dessa classe de maneira diferente: você está certo ao observar que é o código da biblioteca de classes Java que trata esses objetos como clonáveis, serializáveis ​​etc. nada a ver com o compilador ou a JVM.
  2. em vez de usar o operador instanceOf, por que o método não pode ser algo parecido writeObject(Serializable)com o de uma verificação do tipo em tempo de compilação - Isso evita poluir seu código com o nome da interface do marcador quando um "simples Object" é necessário. Por exemplo, se você criar uma classe que precise ser serializável e tenha membros de objeto, você será forçado a converter ou criar seus objetos Serializableem tempo de compilação. Isso é inconveniente, porque a interface é desprovida de qualquer funcionalidade.
  3. Como as anotações são melhores que as interfaces de marcador? - Eles permitem que você alcance o mesmo objetivo de transmitir metadados sobre a classe para seus consumidores sem criar um tipo separado para ela. As anotações também são mais poderosas, permitindo que os programadores passem informações mais sofisticadas para classes que as "consomem".
dasblinkenlight
fonte
14
A maneira como eu sempre entendi é que as anotações são uma espécie de 'Marker Interfaces 2.0': Serializable existe desde o Java 1.1; as anotações foram adicionadas em 1.5
blagae
e porque writeObjecté privado, o que significa que, por exemplo, uma classe não tem que chamar a superclasse implementação
aberração catraca
15
Um aspecto a ter em mente é que as anotações foram adicionadas ao idioma alguns anos depois que a biblioteca padrão foi originalmente projetada. Se as anotações estivessem no idioma desde o início, é duvidoso que o Serializable tivesse sido uma interface, provavelmente teria sido uma anotação.
Theodore Norvell
Bem, no caso de Cloneablenão estar claro se é um tratamento de biblioteca ou JVM da instância que foi alterado.
Holger
"sem criar um tipo separado para ele." - Eu diria que este é precisamente o que os separa: A interface de marcador faz introduzir um tipo, enquanto as anotações (tipo de) does't.
aioobe
22

Não é possível executar Serializableem writeObjectporque as crianças de classe não-serializável pode ser serializado, mas suas instâncias pode estar de volta upcasted para a classe pai. Como resultado, manter uma referência a algo não serializável (como Object) não significa que a instância referida não possa ser realmente serializada. Por exemplo, em

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

a classe pai ( Object) não é serializável e seria inicializada usando seu construtor sem parâmetros. O valor referenciado por x, String, é serializável ea instrução condicional seria executado.

Audrius Meskauskas
fonte
6

Uma interface de marcador em Java é uma interface sem campos ou métodos. Simplificando, uma interface vazia em Java é chamada de interface de marcador. Exemplos de interfaces de marcadores são as Serializable, Cloneablee Remoteinterfaces. Eles são usados ​​para indicar algumas informações para compiladores ou JVMs. Portanto, se a JVM perceber que é uma classe Serializable, poderá executar alguma operação especial nela. Da mesma forma, se a JVM perceber que alguma classe está sendo implementada Cloneable, poderá executar algumas operações para suportar a clonagem. O mesmo vale para o RMI e a Remoteinterface. Portanto, em resumo, uma interface de marcador indica um sinal ou um comando para o compilador ou JVM.

O item acima começou como uma cópia de uma postagem no blog, mas foi ligeiramente editado para gramática.

vidya kn
fonte
6
Está tudo bem em copiar, mas mencione também a fonte: javarevisited.blogspot.com/2012/01/… . Também será bom se você não copiar e colar os erros de ortografia também. :)
Saurabh Patil
6

Fiz uma demonstração simples para resolver a dúvida nº 1 e 2:

Teremos uma interface móvel que será implementada pela MobilePhone.javaclasse e mais uma classe LandlinePhone.javaque não implementa a interface móvel

Nosso marcador Interface:

package com;

public interface Movable {

}

LandLinePhone.java e MobilePhone.java

 package com;

 class LandLinePhone {
    // more code here
 }
 class MobilePhone implements Movable {
    // more code here
 }

Nossa classe de exceção personalizada: 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
    }

Nossa classe de teste: 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");
}}

Agora, quando executamos a classe principal:

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)

Portanto, qualquer classe que implemente a interface do marcador Movablepassará no teste, caso contrário a mensagem de erro será exibida.

É assim instanceOfque a verificação do operador é feita para Serializable , Cloneable etc.

Shashank Bodkhe
fonte
5

a / A interface de marcador, como o próprio nome sugere, existe apenas para notificar qualquer coisa que saiba sobre ela que uma classe declara algo. Qualquer coisa pode ser as classes JDK para a Serializableinterface ou qualquer classe que você escreve para uma customizada.

b / Se for uma interface de marcador, não deve implicar a existência de nenhum método - seria melhor incluir o método implícito na interface. Mas você pode decidir projetá-lo como quiser, se souber por que precisa

c / Há pouca diferença entre uma interface vazia e uma anotação que não usa valor ou parâmetro. Mas a diferença está aí: uma anotação pode declarar uma lista de chaves / valores que estarão acessíveis em tempo de execução.

Serge Ballesta
fonte
3

uma. Eu sempre os vi como um padrão de design e nada de especial da JVM. Usei esse padrão em várias situações.

c. Eu acredito que usar anotações para marcar algo é uma solução melhor do que usar interfaces de marcador. Simplesmente porque as interfaces são, em primeiro lugar, destinadas a definir interfaces comuns de tipos / classes. Eles fazem parte da hierarquia de classes.

As anotações têm como objetivo fornecer meta-informações ao código, e acho que os marcadores são meta-informações. Portanto, eles são exatamente para esse caso de uso.

treeno
fonte
3
  1. Não tem nada a ver (necessariamente) com a JVM e os compiladores, tem algo a ver com qualquer código que esteja interessado e que esteja testando para uma determinada interface de marcador.

  2. É uma decisão de design e é feita por uma boa razão. Veja a resposta de Audrius Meškauskas.

  3. Com relação a esse tópico em particular, não acho que seja uma questão de ser melhor ou pior. A interface do marcador está fazendo o que deveria fazer muito bem.

peter.petrov
fonte
Você pode adicionar um link para "a resposta de Audrius Meškauskas"? Não vejo nada nesta página com esse nome.
Sarah Messer
2

O principal objetivo das interfaces de marcador é criar tipos especiais em que os próprios tipos não têm comportamento próprio.

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

Aqui, o método save garante que apenas os objetos das classes que implementam a interface MarkerEntity sejam salvos, para outros tipos InvalidEntityFoundException. Portanto, aqui a interface do marcador MarkerEntity está definindo um tipo que adiciona comportamento especial às classes que o implementam.

Embora as anotações também possam ser usadas agora para marcar classes para alguns tratamentos especiais, as anotações de marcador substituem o padrão de nomenclatura e não as interfaces do Marcador.

Mas as anotações de marcador não podem substituir completamente as interfaces de marcador porque; interfaces de marcador são usadas para definir o tipo (como já explicado acima), onde as anotações de marcador não.

Origem do comentário da interface do marcador

infoj
fonte
1
+1 por mostrar que é realmente usado como um mero gancho de "segurança" que um programador precisa dizer explicitamente que pode ser salvo no exemplo de um banco de dados.
Ludwig W
1

Eu diria primeiro que Serializable e Cloneable são maus exemplos de interfaces de marcador. Claro, eles são interfaces com métodos, mas implicam métodos, como writeObject(ObjectOutputStream). (O compilador criará um writeObject(ObjectOutputStream)método para você, se você não o substituir, e todos os objetos já o tiverem clone(), mas o compilador criará novamente um clone()método real para você, mas com ressalvas. Ambos são casos extremos estranhos que realmente não são bons exemplos de design.)

As interfaces de marcadores são geralmente usadas para uma de duas finalidades:

1) Como um atalho para evitar um tipo excessivamente longo, o que pode acontecer com muitos genéricos. Por exemplo, digamos que você tenha esta assinatura de método:

public void doSomething(Foobar<String, Map<String, SomethingElse<Integer, Long>>>) { ... }

Isso é confuso e chato de digitar e, mais importante, difícil de entender. Considere isso:

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

Então, seu método fica assim:

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

Não é apenas mais claro, mas agora você pode Javadoc na interface do Widget, e também é mais fácil procurar todas as ocorrências no seu código do Widget.

2) As interfaces de marcadores também podem ser usadas como uma maneira de contornar a falta de tipos de interseção do Java. Com uma interface de marcador, você pode exigir que algo seja de dois tipos diferentes, como em uma assinatura de método. Digamos que você tenha algum widget de interface em seu aplicativo, como descrito acima. Se você possui um método que requer um Widget que também permite iterar sobre ele (é artificial, mas trabalhe comigo aqui), sua única boa solução é criar uma interface de marcador que estenda as duas interfaces:

public interface IterableWidget extends Iterable<String>, Widget { }

E no seu código:

public void doSomething(IterableWidget widget) {
    for (String s : widget) { ... }
}
MikeyB
fonte
1
O compilador não cria nenhum método se sua classe implementar Serializableou Cloneable. Você pode verificá-lo inspecionando seus arquivos de turma. Além disso, a criação de “interfaces de atalho” não é o objetivo das interfaces de marcador. E é uma prática muito ruim de codificação, pois exigiria a criação de classes de implementação adicionais para atender às interfaces adicionais. A propósito, Java tem tipos de interseção há uma década. Aprenda sobre genéricos ...
Holger
Para clonável, você está certo. Isso muda o que Object.clone () faz. Ainda é horrível. Consulte o link Josh Bloch. As interfaces de atalho não são necessariamente um bom padrão de design, pois você restringe arbitrariamente o que pode enviar a um método. Ao mesmo tempo, sinto que escrever um código mais claro, se um pouco mais restritivo, é geralmente uma troca razoável. Quanto aos tipos de interseção, isso se aplica apenas aos genéricos e não pode ser denotado e, portanto, inútil em muitas ocasiões. Tente ter uma variável de instância que seja serializável e, bem, qualquer outra coisa.
MikeyB
0

Se uma interface não contém nenhum método e, ao implementá-la, se nosso objeto tiver alguma habilidade, esse tipo de interface é chamado de interface de marcador.

yogesh kumar
fonte