Java Annotaion


Java annotations are used to provide meta data for your Java code. Being meta data, the annotations do not directly affect the execution of your code, although some types of annotations can actually be used for that purpose.
Java annotations were added to Java from Java 5. This text covers Java annotations as they look in Java 6. As far as I know, Java annotations have not changed in Java 7, so this text should be valid for Java 7 programmers too.

When Not to Use Annotations

  • Do not over use annotation as it will pollute the code.
  • It is better not to try to change the behaviour of objects using annotations. There is sufficient constructs available in oops and annotation is not a better mechanism to deal with it.
  • We should not what we are parsing. Do not try to over generalize as it may complicate the underlying code. Code is the real program and annotation is meta.
  • Avoid using annotation to specify environment / application / database related information.

Annotation Structure

There are two main components in annotations. First is annotation type and the next is the annotation itself which we use in the code to add meaning. Every annotation belongs to a annotation type.
Annotation Type:
@interface  {
method declaration;
}
Annotation type is very similar to an interface with little difference.
  • We attach ‘@’ just before interface keyword.
  • Methods will not have parameters.
  • Methods will not have throws clause.
  • Method return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types.
  • We can assign a default value to method.

Meta Annotations

Annotations itself is meta information then what is meta annotations? As you have rightly guessed, it is information about annotation. When we annotate a annotation type then it is called meta annotation. For example, we say that this annotation can be used only for methods.
@Target(ElementType.METHOD)
public @interface MethodInfo { }

Annotation Types

  1. Documented
    When a annotation type is annotated with @Documented then wherever this annotation is used those elements should be documented using Javadoc tool.
  2. Inherited
    This meta annotation denotes that the annotation type can be inherited from super class. When a class is annotated with annotation of type that is annotated with Inherited, then its super class will be queried till a matching annotation is found.
  3. Retention
    This meta annotation denotes the level till which this annotation will be carried. When an annotation type is annotated with meta annotation Retention, RetentionPolicy has three possible values:
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Developer {
     String value();
    }
    • Class
      When the annotation value is given as ‘class’ then this annotation will be compiled and included in the class file.
    • Runtime
      The value name itself says, when the retention value is ‘Runtime’ this annotation will be available in JVM at runtime. We can write custom code using reflection package and parse the annotation. I have give an example below.
    • SourceThis annotation will be removed at compile time and will not be available at compiled class.
  4. Target
    This meta annotation says that this annotation type is applicable for only the element (ElementType) listed. Possible values for ElementType are, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE.
    @Target(ElementType.FIELD)
    public @interface FieldInfo { }

Built-in Java Annotations

@Documented, @Inherited, @Retention and @Target are the four available meta annotations that are built-in with Java.
Apart from these meta annotations we have the following annotations.
@Override
When we want to override a method, we can use this annotation to say to the compiler we are overriding an existing method. If the compiler finds that there is no matching method found in super class then generates a warning. This is not mandatory to use @Override when we override a method. But I have seen Eclipse IDE automatically adding this @Override annotation. Though it is not mandatory, it is considered as a best practice.
@Deprecated
When we want to inform the compiler that a method is deprecated we can use this. So, when a method is annotated with @Deprecated and that method is found used in some place, then the compiler generates a warning.
@SuppressWarnings
This is like saying, “I know what I am doing, so please shut up!” We want the compiler not to raise any warnings and then we use this annotation.

Custom Annotations

We can create our own annotations and use it. We need to declare a annotation type and then use the respective annotation is java classes.
Following is an example of custom annotation, where this annotation can be used on any element by giving values. Note that I have used @Documented meta-annotation here to say that this annotation should be parsed by javadoc.
/*
 * Describes the team which wrote the code
 */
@Documented
public @interface Team {
    int    teamId();
    String teamName();
    String teamLead() default "[unassigned]";
    String writeDate();    default "[unimplemented]";
}

Annotation for the Above Example Type

... a java class ...
@Team(
    teamId       = 73,
    teamName = "Rambo Mambo",
    teamLead = "Yo Man",
    writeDate     = "3/1/2012"
)
public static void readCSV(File inputFile) { ... }
... java class continues ...

Marker Annotations

We know what a marker interface is. Marker annotations are similar to marker interfaces, yes they don’t have methods / elements.
/**
 * Code annotated by this team is supreme and need
 * not be unit tested - just for fun!
 */
public @interface SuperTeam { }
... a java class ...
@SuperTeam public static void readCSV(File inputFile) { ... }
... java class continues ...
In the above see how this annotation is used. It will look like one of the modifiers for this method and also note that the parenthesis () from annotation type is omitted. As there are no elements for this annotation, the parenthesis can be optionally omitted.

Single Value Annotations

There is a chance that an annotation can have only one element. In such a case that element should be named value.
/**
 * Developer
 */
public @interface Developer {
    String value();
}
... a java class ...
@Developer("Popeye")
public static void readCSV(File inputFile) { ... }
... java class continues ...

How to Parse Annotation

We can use reflection package to read annotations. It is useful when we develop tools to automate a certain process based on annotation.
Example:
package com.javapapers.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Developer {
 String value();
}
package com.javapapers.annotations;
public class BuildHouse {
 @Developer ("Alice")
 public void aliceMethod() {
  System.out.println("This method is written by Alice");
 }
 @Developer ("Popeye")
 public void buildHouse() {
  System.out.println("This method is written by Popeye");
 }
}
package com.javapapers.annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class TestAnnotation {
 public static void main(String args[]) throws SecurityException,
   ClassNotFoundException {
  for (Method method : Class.forName(
    "com.javapapers.annotations.BuildHouse").getMethods()) {
   // checks if there is annotation present of the given type Developer
   if (method
     .isAnnotationPresent(com.javapapers.annotations.Developer.class)) {
    try {
     // iterates all the annotations available in the method
     for (Annotation anno : method.getDeclaredAnnotations()) {
      System.out.println("Annotation in Method '" + method
        + "' : " + anno);
      Developer a = method.getAnnotation(Developer.class);
      if ("Popeye".equals(a.value())) {
       System.out.println("Popeye the sailor man! "
         + method);
      }
     }
    } catch (Throwable ex) {
     ex.printStackTrace();
    }
   }
  }
 }
}
Output:
Annotation in Method 'public void com.javapapers.annotations.BuildHouse.aliceMethod()' : @com.javapapers.annotations.Developer(value=Alice)
Annotation in Method 'public void com.javapapers.annotations.BuildHouse.buildHouse()' : @com.javapapers.annotations.Developer(value=Popeye)
Popeye the sailor man! public void com.javapapers.an
you can see the xml vs anootation

@Test Annotation

This @interface tells Java this is a custom annotation. Later, you can annotate it on method level like this@Test(enable=false).
Test.java
package com.mkyong.test.core;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) //can use in method only.
public @interface Test {
 
 //should ignore this test?
 public boolean enabled() default true;
 
}
Note Method declarations must not have any parameters or a throws clause. Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types.

@TesterInfo Annotation

This @TesterInfo is applied on class level, store the tester details. This shows the different use of return types – enum, array and string.
TesterInfo.java
package com.mkyong.test.core;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) //on class level
public @interface TesterInfo {
 
 public enum Priority {
    LOW, MEDIUM, HIGH
 }
 
 Priority priority() default Priority.MEDIUM;
 
 String[] tags() default "";
 
 String createdBy() default "Mkyong";
 
 String lastModified() default "03/01/2014";
 
}

Unit Test Example

Create a simple unit test example, and annotated with the new custom annotations – @Test and @TesterInfo.
TestExample.java
package com.mkyong.test;
 
import com.mkyong.test.core.Test;
import com.mkyong.test.core.TesterInfo;
import com.mkyong.test.core.TesterInfo.Priority;
 
@TesterInfo(
 priority = Priority.HIGH, 
 createdBy = "mkyong.com",  
 tags = {"sales","test" }
)
public class TestExample {
 
 @Test
 void testA() {
   if (true)
  throw new RuntimeException("This test always failed");
 }
 
 @Test(enabled = false)
 void testB() {
   if (false)
  throw new RuntimeException("This test always passed");
 }
 
 @Test(enabled = true)
 void testC() {
   if (10 > 1) {
  // do nothing, this test always passed.
   }
 }
 
}
Ref:Mkyoung.


No comments:

Post a Comment