The Learning the Java Language trail moves onto Annotations. Annotations are a form of metadata that can provide data about the program and there is more to them than you might think…

Overview

  • Info the compiler, e.g. for detecting errors or suppressing warnings.
  • Info that can be used at compile time or for deployment processing.
  • Info that can be examined at runtime.
  • Applied to declarations, e.g. classes, fields,methods, etc.
@Annotation 
@AnnoWithOneElement("elementValue") 
@AnnoWithOneElement("elementValue2") // Repeating annotation in Java SE 8
class MyClass() {

  @AnnoWithElements(
     element1 = "value1",
     element2 = "value2"
  )
  void myMethod() { }

}

 

Declaring An Annotation Type

(not to be confused with Type Annotation – haha)

// import this to use @Documented in Javadocs
import java.lang.annotation.*;

@Documented // To javadoc our Annotation Type
@interface ClassPreamble { // Define Annotation as an interface
   String author(); // An annotation type element
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}
@ClassPreamble (
   author = "John Doe",
   date = "3/17/2002",
   currentRevision = 6,
   lastModified = "4/12/2004",
   lastModifiedBy = "Jane Doe",
   // Note array notation
   reviewers = {"Alice", "Bob", "Cindy"}
)
public class MyClass extends MySuperClass { ...

Predefined Annotations

// Javadoc comment follows
/**
 * @deprecated
 * explanation of why it was deprecated
 */
@Deprecated // compiler creates warning
static void deprecatedMethod() { }

-Xlint:deprecation : compiler option shows deprecated code usage

C:...\src\Annotations\House.java:22: warning: 
[deprecation] open() in House has been deprecated
 public void open() {}
// mark method as a superclass method that has been overridden
// helps to prevent errors
@Override 
int overriddenMethod() { }
// deprecation : suppress warning about a deprecated method
// unchecked : suppress warning about code predating generics
@SuppressWarnings({"deprecation","unchecked"})
  void useDeprecatedMethod() {
    objectOne.deprecatedMethod();
}
  • @SafeVarargs : suppresses unchecked warnings relating to varargs.

This one is rather glossed over in the tutorial and for good reason. Examples of it involving discussing non-reifiable types and generics which are probably out of scope right here.

Indicate that a type declaration is intended to be a functional interface in Java SE 8. You see this standard functional interfaces.

Annotations That Apply To Other Annotations

Meta-annotations that apply to other annotations, e.g. in java.lang.annotation.

  • @Retention  – specifies how the marked annotation is stored
    • RetentionPolicy.SOURCE – ignored by compiler, only in the source code
    • RetentionPolicy.CLASS – retained at compile time but ignored by JVM
    • RetentionPolicy.RUNTIME – retained at runtime
  • @Documented – the annotation should be documented by javadoc or other tools
  • @Target – restricts what Java elements the annotation can be applied to
    • ElementType.ANNOTATION_TYPE
    • ElementType.CONSTRUCTOR
    • ElementType.FIELD or property
    • ElementType.LOCAL_VARIABLE
    • ElementType.METHOD
    • ElementType.PACKAGE
    • ElementType.PARAMETER of a method
    • ElementType.TYPE – any element of a class
  • @Inherited – indicates the annotation is automatically inherited, subclasses of annotated classes are considered having the same annotation as their superclass.
    import java.lang.annotation.*;
    
    @Inherited
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface InheritedAnnotation {}
    
    // Not @Inherited
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface UninheritedAnnotation {}
    
    
    @UninheritedAnnotation
    class A {}
    
    @InheritedAnnotation
    class B extends A {}
    
    class C extends B {}
    
    public class InheritedAnnotation {
      public static void main (String[] args) {
        System.out.println("A InheritedAnnotation: " + new 
         A().getClass().getAnnotation(InheritedAnnotation.class));
        System.out.println("subclass B InheritedAnnotation: " + new
         B().getClass().getAnnotation(InheritedAnnotation.class));
        System.out.println("subclass C InheritedAnnotation: " + new
         C().getClass().getAnnotation(InheritedAnnotation.class));
        System.out.println("_________________________________");
        System.out.println("A UninheritedAnnotation: " + new
         A().getClass().getAnnotation(UninheritedAnnotation.class));
        System.out.println("subclass B UninheritedAnnotation: " + new
         B().getClass().getAnnotation(UninheritedAnnotation.class));
        System.out.println("subclass C UninheritedAnnotation: " + new
         C().getClass().getAnnotation(UninheritedAnnotation.class));
      }
    }
    
    A InheritedAnnotation: null
    subclass B InheritedAnnotation: @Annotations.InheritedAnnotation()
    subclass C InheritedAnnotation: @Annotations.InheritedAnnotation()
    _________________________________
    A UninheritedAnnotation: @Annotations.UninheritedAnnotation()
    subclass B UninheritedAnnotation: null
    subclass C UninheritedAnnotation: null
    
  • @Repeatable – annotation can be applied more than once ( Java SE 8 )
    • Stored in a container annotation.
// Note @Repeatable requires an element called value
// public abstract Class<? extends Annotation> value
// Which indicates the containing annotation type for the
// repeatable annotation type. In this example repeating Schedule
// annotations are stored in a @Schedules annotation
@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

// Container annotation must have a value array of Schedule
public @interface Schedules {
    Schedule[] value();
}

 

Java SE 8 Type Annotations and Pluggable Type Systems

  • Prior to Java SE 8 annotations can only be applied to declarations
  • Post Java SE 8 annotations can be applied to any type use, e.g. class instance creation expressions ( new ), implements, throws
  • Allows stronger type checking via the use of type checking frameworks (e.g. Check Framework) as pluggable modules used in conjunction with the Java compiler.
// Class instantiation
new @Interned MyObject();
// Type cast
myString = (@NonNull String) str
// Implements
class UnmodifiableList implements @Readonly List<@Readonly T> {
// Thrown exception declaration
void monitorTemperature() throws @Critical TemperatureException {

 

Exercises

Questions

  1. What is wrong with the following interface?
    public interface House {
      /**
       * @deprecated
      * There should be an explanation of why it was deprecated
      */
      @Deprecated
      void open();
      void openFrontDoor();
      void openBackDoor();
    }
    
  2. Consider this implementation of the House interface, shown in Question 1.
    class MyHouse implements House {
        @Override // Also needs this annotation to suppress warnings
        @SuppressWarnings("deprecation") // Suppress warnings   
        @Deprecated // Or deprecate the implementation of the method
        public void open() {}
        @Override
        public void openFrontDoor() {}
        @Override
        public void openBackDoor() {}
    

    If you compile this program, the compiler produces a warning because open was deprecated (in the interface). What can you do to get rid of that warning?

  3. Will the following code compile without error? Why or why not?
    // Import required to use Repeatable
    import java.lang.annotation.*;
    
    // The annotation declaration does not include any elements
    // and is not Repeatable
    public @interface Meal { ... }
    
    // Should be
    @Repeatable(Meals.class)
    @interface Meal2 { 
        String meal();
        String mainDish();
    }
    // And also need to define a container annotation as it is Repeatable
    @interface Meals {
        Meal2[] value();
    }
    
    @Meal("breakfast", mainDish="cereal")
    @Meal("lunch", mainDish="pizza")
    @Meal("dinner", mainDish="salad")
    public void evaluateDiet() { ... }
    
    // The annotation needs to include the elements
    @Meal2(meal="breakfast", mainDish="cereal")
    @Meal2(meal="lunch", mainDish="pizza")
    @Meal2(meal="dinner", mainDish="salad")
    public void evaluateDiet2() { }
    

Exercises

  1. Define an annotation type for an enhancement request with elements id, synopsis, engineer, and date. Specify the default value as unassigned for engineer andunknown for date.
@interface EnhancementRequest { 
    int id();
    String synposis();
    String engineer() default "unassigned";    
    String date() default "unknown";
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s