Bits of Java – Episode 18: Java Annotations

This week we will talk about another interesting feature of the Java language, annotations.

What are annotations? Well, you can think of them like a convenient way to express meta data of your objects, where, with objects here, I mean not only Java objects, but also methods, fields, and also other annotations.

To define your own annotation you can just write something like:


public @interface MyAnnotation {
    public String name() default "Hi"; //optional element
    int number;  //required element
    String[] greetings();
    String SOME_CONST = "Ilenia";
}

Let’s examine the various parts.

  • To defined that you want to create an annotation you use @interface

  • An annotation can have

    elements

    , which are implicitly

    public

    and can be declared with their type and their name, followed by

    ()
    

    . Elements can be

    optional

    or

    mandatory

    . Optional element declaration is followed by the keyword

    default
    

    plus their default value, which should be a non-null constant expression. Not every Java type is allowed for an annotation element. You can have:

    • primitive types;
    • String;
    • Enum;
    • annotations;
    • Class objects;
    • one dimensional array of any of the previous types.
  • An annotation can also have other fields, which are implicitly considered public static final

That said, it may be easier to understand these rules while looking at some invalid annotation declarations.


public @interface MyWrongAnnotation {
    //INVALID: elements are implicitly public
    private String name();

    //INVALID: missing () after element name
    String anotherName default "Hi";

    //INVALID new String() is not a constant expression
    //evaluated at compile time, so is not allowed
    String surname() default new String("MySurname");

    //INVALID: wrapper class are not allowed as
    //annotation element types
    Integer number();

    //INVALID: interfaces are not allowed as 
    //annotation element types
    List<String> names();

    //INVALID: fields are implicitly public static final
    private String something = "Something";
}

Now that we know how to declare our custom annotations, how to use them? We have said before that we can apply annotations to a wide variety of objects, like Java classes, interfaces, methods, and even other annotations. To use an annotation you should do something along the line of:


@MyAnnotation(number = 3, greetings = {"Hi", "Hola"}, name = "Ilenia")
public class MyClass {
    @MyAnnotation(number = 5, greetings = "Ciao") String someStr;

    @MyAnnotation(number = 7, greetings = {"Salut"}, name = "Andrea")
    public void myMethod() {
        //do something
    }
}

Here we have applied the annotation we defined at the beginning of our discussion to three different elements: a class, a field and a method. As you can see, the annotation is applied in the same way in all three cases.

  • Mandatory elements need to be set (as we did with the element number in all three cases);
  • Optional elements are not required to be set (notice the second case with the element name not set). If not set, the default value will be assumed;
  • Elements which require an array of elements can be set in different ways:
    • if more than one value need to be set than we use the syntax {value1, value2} (see our first case);
    • if just one value needs to be set we can use both {value1} or simply value1;

There is an additional rule, which applies only in certain situations. In particular, if you have an annotation which has only one required element, called value, and this is the only element you want to set when using the annotation, then you can do something like:


public @interface MySecondAnnotation {
    String value(); //this is the only required element
    int number() default 7;
}

@MySecondAnnotation("Hola")
class MyClass {
    //do something
}

As you can see, when using the annotation, we did not write value="Hola", but simply "Hola".

Now you basically know all you need to know to start creating and using your annotations. But, you may also find pretty useful some built-in Java annotations.

  • @FunctionalInterface: this is a marker annotation (it does not have any element), and you can use it to annotate an interface which is a functional interface. To be a functional interface, it needs to declare one and only one abstract method, with the exception of methods with the same signature as the ones in the Object class.
  • @Deprecated: this is very useful if you need to mark something which probably will be replaced soon, to warn the other developers that it’s not the best choice to use the object marked this way, as it likely exists already a more recent version of it. There are not mandatory elements you need to set when using this annotation, but you can specify a since element, and a forRemoval, to say from where the object is deprecated and whether it will be removed soon or not.
  • @SuppressWarnings: it happens sometimes that you are coding something that the compiler does not recognize as super secure, like using raw types, when you could have used generics, or using deprecated methods. If you, instead, know exactly what you are doing and there is no harm in it, you can suppress the compiler warnings with this annotation. For removing the warnings due to our two examples, you can use SuppressWarnings("unchecked") and SuppressWarnings(“deprecation”), respectively.
  • @SafeVarargs: this annotation can be used for constructors and method declarations which cannot be overridden (static, final and private), which have a varargs as method parameter. The annotation indicates that you are not performing any unsafe operation on the varargs. If you are actually doing it or not is really up to you. What you are saying to your fellows programmers is that you know what you are doing and they should not be expecting any strange behavior from your method.

I hope you found this topic interesting! Java Enumerators are coming up next!

by Ilenia Salvadori