Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use cases for implementing annotations

What are valid use cases for implementing annotations?

When designing primarily annotation based configuration systems I occasionally need to create classes which implement annotations for code generation or programmatic configuration.

The alternative involves mirroring the data contained in annotations into DTOs, which seems like an overhead.

Here is an example:

public enum IDType {
    LOCAL,
    URI,
    RESOURCE;
}

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

with the implementation

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

}

I get compiler warnings for this, but it seems to be a valid tool for many use cases.

The warning for the example above is

The annotation type Id should not be used as a superinterface for IdImpl

Edited :

I just found this example from Guice :

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

See this Javadoc from Names.

Has anyone some information why this restriction exists or has some other use cases in mind?

like image 703
Timo Westkämper Avatar asked Jul 27 '10 08:07

Timo Westkämper


People also ask

What are the uses of annotation?

Annotations have a number of uses, among them: Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings. Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.

Are Java annotations useful?

These annotations generate or suppress compiler warnings and errors. Applying them consistently is often a good practice since adding them can prevent future programmer error. The @Override annotation is used to indicate that a method overrides or replaces the behavior of an inherited method.


2 Answers

I've never used it in practice but what you get is, that you can use classes as replacement for your annotations.

Let's create an artificial example. Say we have an documentation generator. It reads a @Docu annotation from given classes and prints the description attribute. Like this:

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 {
}

Prints:

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

What we now want to accomplish is, that a class can not only be staticly annotated, but can add runtime information to the documentation. We are quite happy to use the @Docu annotation most of the time, but there are special cases we want special documenation. We might want to add performance documenation for some methodes. We can do this by letting a class implement the annotation. The generator checks first for the annotation and, if not present, it checks if the class implements the annotation. If it does, it adds the class to the list of annotations.

Like this (only two additional lines of code in the generator):

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

}

Output is:

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.

You havn't to change the code generator that much because you can use the class as replacement of the annotation.

Other example whould be a framework that uses annotations or XML as configuration. You might have one processor that works on annotations. If you use XML as configuration you can generate instances of classes that implement the annotations and your processor works on them without a single change! (of course there are other ways to accomplish the same effect, but this is ONE way to do it)

like image 103
Arne Deutsch Avatar answered Oct 04 '22 12:10

Arne Deutsch


JAXBIntroductions is a good example: it allows configuring JAXB annotations using XML files. Two main use cases come to mind: configuring classes you don't have source access or different configurations for one class.

In general, I think instantiating annotations dynamically to pass them to frameworks is generally a good use case. However, if you are the designer of this framework, I certainly would think twice though.

like image 27
sfussenegger Avatar answered Sep 23 '22 12:09

sfussenegger