2010-07-27 24 views
34

¿Cuáles son casos de uso válidos para implementar anotaciones?Casos de uso para implementar anotaciones

Al diseñar principalmente sistemas de configuración basados ​​en anotaciones, ocasionalmente necesito crear clases que implementen anotaciones para la generación de código o la configuración programática.

La alternativa consiste en duplicar los datos contenidos en las anotaciones en DTO, lo que parece una sobrecarga.

Aquí se muestra un ejemplo:

public enum IDType { 
    LOCAL, 
    URI, 
    RESOURCE; 
} 

@Documented 
@Target({ METHOD, FIELD }) 
@Retention(RetentionPolicy.RUNTIME) 
@Inherited 
public @interface Id { 
    /** 
    * @return 
    */ 
    IDType value() default IDType.LOCAL; 
} 

con la implementación

public class IdImpl implements Id{ 

    private final IDType idType; 

    public IdImpl(IDType idType){ 
     this.idType = idType; 
    } 

    @Override 
    public IDType value() { 
     return idType; 
    } 

    @Override 
    public Class<? extends Annotation> annotationType() { 
     return Id.class; 
    } 

} 

consigo las advertencias del compilador para esto, pero parece ser una herramienta válida para muchos casos de uso.

La advertencia para el ejemplo anterior es

El tipo de anotación Id no debe ser utiliza como superinterfaz para IdImpl

Editado:

I acaba de encontrar este ejemplo de Guice :

bind(CreditCardProcessor.class) 
    .annotatedWith(Names.named("Checkout")) 
    .to(CheckoutCreditCardProcessor.class); 

Vea esto Javadoc from Names.

¿Alguien tiene alguna información sobre por qué existe esta restricción o tiene otros casos de uso en mente?

+2

¿Qué advertencias tienes? – djna

+2

@djina: Sí, un centavo por cada vez que tienes que decir eso y serás rico. Nunca deja de sorprenderme. – musiKk

+0

Quizás sea solo yo, pero parece estar estrechamente relacionado con esta pregunta: http: // stackoverflow.com/questions/1624084/why-is-not-possible-to-extend-annotations-in-java –

Respuesta

19

Nunca lo he usado en la práctica, pero lo que obtienes es que puedes usar clases como reemplazo para tus anotaciones.

Vamos a crear un ejemplo artificial. Digamos que tenemos un generador de documentación. Lee una anotación @Docu de clases dadas e imprime el atributo description. De esta manera:

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.util.ArrayList; 
import java.util.List; 

public class DokuGenerator { 

    public static void main(String[] args) throws Exception { 
     new DokuGenerator(StaticClass.class, StaticClass2.class); 
    } 

    public DokuGenerator(Class<?>... classesToDokument) throws Exception { 
     List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument); 
     printDocumentation(documentAnnotations); 
    } 

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument) 
      throws Exception { 
     List<Docu> result = new ArrayList<Docu>(); 
     for (Class<?> c : classesToDokument) 
      if (c.isAnnotationPresent(Docu.class)) 
       result.add(c.getAnnotation(Docu.class)); 
     return result; 
    } 

    private void printDocumentation(List<Docu> toDocument) { 
     for (Docu m : toDocument) 
      System.out.println(m.description()); 
    } 

} 

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@interface Docu { 
    String description(); 
} 

@Docu(description = "This is a static class!") 
class StaticClass { 
} 

@Docu(description = "This is another static class!") 
class StaticClass2 { 
} 

Lienzo:

This is a static class! 
This is another static class! 

Lo que ahora queremos lograr es, que una clase no sólo se puede staticly anotado, pero puede añadir información de tiempo de ejecución para la documentación. Estamos muy contentos de utilizar la anotación @Docu la mayor parte del tiempo, pero hay casos especiales en los que queremos una documentación especial. Es posible que deseemos agregar documentacion de rendimiento para algunos metodos. Podemos hacer esto permitiendo que una clase implemente la anotación. El generador verifica primero la anotación y, si no está presente, verifica si la clase implementa la anotación. Si lo hace, agrega la clase a la lista de anotaciones.

como esto (sólo dos líneas de código adicionales en el generador):

import java.lang.annotation.Annotation; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

public class DokuGenerator { 

    public static void main(String[] args) throws Exception { 
     new DokuGenerator(StaticClass.class, StaticClass2.class, 
       DynamicClass.class); 
    } 

    public DokuGenerator(Class<?>... classesToDokument) throws Exception { 
     List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument); 
     printDocumentation(documentAnnotations); 
    } 

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument) 
      throws Exception { 
     List<Docu> result = new ArrayList<Docu>(); 
     for (Class<?> c : classesToDokument) 
      if (c.isAnnotationPresent(Docu.class)) 
       result.add(c.getAnnotation(Docu.class)); 
      else if (Arrays.asList(c.getInterfaces()).contains(Docu.class)) 
       result.add((Docu) c.newInstance()); 
     return result; 
    } 

    private void printDocumentation(List<Docu> toDocument) { 
     for (Docu m : toDocument) 
      System.out.println(m.description()); 
    } 

} 

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@interface Docu { 
    String description(); 
} 

@Docu(description = "This is a static class!") 
class StaticClass { 
} 

@Docu(description = "This is another static class!") 
class StaticClass2 { 
} 

class DynamicClass implements Docu { 

    public DynamicClass() { 
     try { 
      Thread.sleep((long) (Math.random() * 100)); 
     } catch (InterruptedException e) { 
      // ignore exception to make debugging a little harder 
     } 
    } 

    @Override 
    public String description() { 
     long millis = System.currentTimeMillis(); 
     new DynamicClass(); 
     millis = System.currentTimeMillis() - millis; 
     return "This is a dynamic class. I run on " 
       + System.getProperty("os.name") 
       + ". The construction of an instance of this class run for " 
       + millis + " milliseconds."; 
    } 

    @Override 
    public Class<? extends Annotation> annotationType() { 
     return Docu.class; 
    } 

} 

salida es:

This is a static class! 
This is another static class! 
This is a dynamic class. I run on Windows XP. The construction of an instance of this class run for 47 milliseconds. 

usted no ha de cambiar el generador de código que gran parte porque se puede utilizar la clase como reemplazo de la anotación.

Otro ejemplo sería un marco que usa anotaciones o XML como configuración. Es posible que tenga un procesador que funcione con anotaciones. Si usa XML como configuración, puede generar instancias de clases que implementen las anotaciones y su procesador trabaje en ellas sin un solo cambio. (por supuesto, hay otras formas de lograr el mismo efecto, pero esta es UNA forma de hacerlo)

+0

gracias por la respuesta detallada. Las clases como reemplazos para las instancias de anotación derivadas de la reflexión son un caso válido. Las anotaciones son bastante útiles como datos de configuración y para los casos dinámicos que los ejemplifican es necesario. –

+0

"// ignorar la excepción para hacer que la depuración sea un poco más difícil" +1 – Izmaki

-1

No hay casos de usuarios válidos para eso - el compilador simplemente lo tolera, ya que sería bastante complicado prohibirlo y las personas que escriben compiladores pueden necesitar el servicio en muy raras ocasiones. Si necesita clasificar anotaciones consulte este artículo para ver cómo hacerlo: Why is not possible to extend annotations in Java?

Inagine un alma pobre que viene detrás de usted para mantener y depurar ese código u otro que necesite escribir una herramienta Codegen y asume que los tipos de anotación son heterosexuales u otra persona que acaba de usar dicha anotación sin siquiera soñar qué puede suceder y qué hacer al respecto. Para cuando descubra ese truco y encuentre la manera de eliminarlo, va a morir de hernia, o dolencia equivalente :-) Se espera que las anotaciones sean declaraciones puramente declarativas, interpretadas únicamente por una herramienta Codegen que se ejecuta por separado del código anotado y lo trata como datos.

dar una nueva mirada a ese código y tratan de decir sinceramente lo que es una rason racional para algo como:

public Class<? extends Annotation> annotationType() { 
    return Id.class; 
} 

y eso es stil una cosa pequeña en comparación con el que la gente puede poner en el código.

Las anotaciones no son el lugar para practicar la piratería, eso es lo que el compilador intenta transmitir. ¿Sabes exactamente cuándo y cómo se puede ejecutar el código en la "implementación" de la anotación? Incluyendo CTOR? ¿Qué está disponible y qué no en ese punto? ¿Qué es seguro llamar? El compilador no lo hace: se necesitaría un análisis estático bastante pesado para que un compilador verifique la seguridad real de tal pirateo. Por lo tanto, en su lugar solo emite una advertencia para que cuando algo sale mal, la gente no pueda culpar a la compilación, VM y todo lo demás.

+0

No estoy interesado en clasificar anotaciones y mis casos de uso no están relacionados con los compiladores. Y no considero que mis casos de uso sean "piratería". –

+0

@ZZX, su ejemplo faltaba el javadoc que, con suerte, contiene la respuesta a _why? _. –

+0

Su reacción exagerada no está garantizada. Aquí hay un caso de uso muy válido para implementar anotaciones: Desea ejecutar procesadores de anotación que suministren metadatos utilizando una API que no sea de la suya, como javax.lang.model, que se libera al tratar de cargar valores de clase. Es mejor escribir un procesador de anotaciones que escanee anotaciones y genere un generador de anotaciones que realmente pueda operar en los valores de clase. (javax.lang.model lo obliga a atrapar una excepción de tiempo de ejecución no declarado y extraer el tipo de clase de un espejo de tipo; el generador generado se ocupa de ese feo lío en otros procesadores de anotaciones). – Ajax

4

JAXBIntroductions es un buen ejemplo: permite configurar anotaciones JAXB utilizando archivos XML. Me vienen a la mente dos casos de uso principales: configuración de clases en las que no tienes acceso de fuente o diferentes configuraciones para una clase.

En general, creo que crear instancias de anotaciones dinámicamente para pasarlas a frameworks es generalmente un buen caso de uso. Sin embargo, si usted es el diseñador de este marco, sin duda lo pensaría dos veces.

0

Lo uso cuando he creado una anotación y quiero que su uso sea opcional, proporcionando un valor predeterminado.

Para usar su ejemplo; Cuando proceso/introspejo los siguientes beans, quiero usar un valor predeterminado para BeanB.

@Id 
class BeanA {} 

// No annotation 
class BeanB {} 

La implementación predeterminada;

private static final Id DEFAULT_ID = new Id() { 

    @Override 
    public IDType value() { 
     return IDType.LOCAL; 
    } 

    @Override 
    public Class<? extends Annotation> annotationType() { 
     return Id.class; 
    } 
}; 

Procesamiento;

Id beanId = (bean.getClass().isAnnotationPresent(Id.class)) 
    ? bean.getClass().getAnnotation(Id.class) 
    : DEFAULT_ID; 
Cuestiones relacionadas