Chapter: Java The Complete Reference - The Java Language - Enumerations, Autoboxing, and Annotations (Metadata)

| Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail |

Type Annotations

Beginning with JDK 8, the places in which annotations can be used has been expanded. As mentioned earlier, annotations were originally allowed only on declarations.

Type Annotations

 

Beginning with JDK 8, the places in which annotations can be used has been expanded. As mentioned earlier, annotations were originally allowed only on declarations. However, with the advent of JDK 8, annotations can also be specified in most cases in which a type is used. This expanded aspect of annotations is called type annotation. For example, you can annotate the return type of a method, the type of this within a method, a cast, array levels, an inherited class, and a throws clause. You can also annotate generic types, including generic type parameter bounds and generic type arguments. (See Chapter 14 for a discussion of generics.)

Type annotations are important because they enable tools to perform additional checks on code to help prevent errors. Understand that, as a general rule, javac will not perform these checks, itself. A separate tool is used for this purpose, although such a tool might operate as a compiler plug-in.

 

A type annotation must include ElementType.TYPE_USE as a target. (Recall that valid annotation targets are specified using the @Target annotation, as previously described.) A type annotation applies to the type that the annotation precedes. For example, assuming some type annotation called @TypeAnno, the following is legal:

 

void myMeth() throws @TypeAnno NullPointerException { // ...

 

Here, @TypeAnno annotates NullPointerException in the throws clause.

 

You can also annotate the type of this (called the receiver). As you know, this is an implicit argument to all instance methods and it refers to the invoking object. To annotate its type requires the use of another new JDK 8 feature. Beginning with JDK 8, you can explicitly declare this as the first parameter to a method. In this declaration, the type of this must be the type of its class; for example:

 

class SomeClass {

 

int myMeth(SomeClass this, int i, int j) { // ...

 

Here, because myMeth( ) is a method defined by SomeClass, the type of this is SomeClass. Using this declaration, you can now annotate the type of this. For example, again assuming that @TypeAnno is a type annotation, the following is legal:

 

int myMeth(@TypeAnno SomeClass this, int i, int j) { // ...

 

It is important to understand that it is not necessary to declare this unless you are annotating it. (If this is not declared, it is still implicitly passed. JDK 8 does not change this fact.) Also, explicitly declaring this does not change any aspect of the method’s signature because this is implicitly declared, by default. Again, you will declare this only if you want to apply a type annotation to it. If you do declare this, it must be the first parameter.

 

The following program shows a number of the places that a type annotation can be used. It defines several annotations, of which several are for type annotation. The names and targets of the annotations are shown here:


 

Notice that @EmptyOK, @Recommended, and @What are not type annotations. They are included for comparison purposes. Of special interest is @What, which is used to annotate a generic type parameter declaration and is another new annotation feature added by JDK 8. The comments in the program describe each use.

 

     Demonstrate several type annotations.

     import java.lang.annotation.*;

import java.lang.reflect.*;

 

     //A marker annotation that can be applied to a type.

      @Target(ElementType.TYPE_USE)

 

@interface TypeAnno { }

 

     //Another marker annotation that can be applied to a type.

     @Target(ElementType.TYPE_USE)

 

@interface NotZeroLen {

 

}

 

     //Still another marker annotation that can be applied to a type.

     @Target(ElementType.TYPE_USE)

 

@interface Unique { }

 

     //A parameterized annotation that can be applied to a type. @Target(ElementType.TYPE_USE)

 

@interface MaxLen { int value();

 

}

 

     //An annotation that can be applied to a type parameter.

     @Target(ElementType.TYPE_PARAMETER)

 

@interface What { String description();

}

 

     //An annotation that can be applied to a field declaration.

     @Target(ElementType.FIELD)

 

@interface EmptyOK { }

 

     //An annotation that can be applied to a method declaration.

     @Target(ElementType.METHOD)

 

@interface Recommended { }

 

 

     //Use an annotation on a type parameter.

 

class TypeAnnoDemo<@What(description = "Generic data type") T> {

 

     Use a type annotation on a constructor. public @Unique TypeAnnoDemo() {}

 

Annotate the type (in this case String), not the field. @TypeAnno String str;

     This annotates the field test. @EmptyOK String test;

 

     //Use a type annotation to annotate this (the receiver).

 

     public int f(@TypeAnno TypeAnnoDemo<T> this, int x) {

 

return 10;

 

}

 

     //Annotate the return type.

 

public @TypeAnno Integer f2(int j, int k) { return j+k;

 

}

 

// Annotate the method declaration.

 

public @Recommended Integer f3(String str) { return str.length() / 2;

 

}

 

// Use a type annotation with a throws clause.

 

public void f4() throws @TypeAnno NullPointerException { // ...

 

}

 

// Annotate array levels.

 

String @MaxLen(10) [] @NotZeroLen [] w;

 

// Annotate the array element type. @TypeAnno Integer[] vec;

 

public static void myMeth(int i) {

 

// Use a type annotation on a type argument.

TypeAnnoDemo<@TypeAnno Integer> ob =

 

new TypeAnnoDemo<@TypeAnno Integer>();

 

// Use a type annotation with new.

 

@Unique TypeAnnoDemo<Integer> ob2 = new

@Unique TypeAnnoDemo<Integer>();

 

Object x = new Integer(10);

 

Integer y;

 

// Use a type annotation on a cast.

y = (@TypeAnno Integer) x;

 

}

 

public static void main(String args[]) { myMeth(10);

 

}

 

// Use type annotation with inheritance clause.

 

class SomeClass extends @TypeAnno TypeAnnoDemo<Boolean> {}

Although what most of the annotations in the preceding program refer to is clear, four uses require a bit of discussion. The first is the annotation of a method return type versus the annotation of a method declaration. In the program, pay special attention to these two method declarations:

 

 

// Annotate the return type.

 

public @TypeAnno Integer f2(int j, int k) { return j+k;

 

}

 

// Annotate the method declaration.

 

public @Recommended Integer f3(String str) { return str.length() / 2;

 

}

 

Notice that in both cases, an annotation precedes the method’s return type (which is Integer). However, the two annotations annotate two different things. In the first case, the @TypeAnno annotation annotates f2( )’s return type. This is because @TypeAnno has its target specified as ElementType.TYPE_USE, which means that it can be used to annotate type uses. In the second case, @Recommended annotates the method declaration, itself. This is because @Recommended has its target specified as ElementType.METHOD. As a result, @Recommended applies to the declaration, not the return type. Therefore, the target specification is used to eliminate what, at first glance, appears to be ambiguity between the annotation of a method declaration and the annotation of the method’s return type.

 

One other thing about annotating a method return type: You cannot annotate a return type of void.

The second point of interest are the field annotations, shown here:

 

     Annotate the type (in this case String), not the field.

      @TypeAnno String str;

 

     This annotates the field test.

 

@EmptyOK String test;

 

Here, @TypeAnno annotates the type String, but @EmptyOK annotates the field test. Even though both annotations precede the entire declaration, their targets are different, based on the target element type. If the annotation has the ElementType.TYPE_USE target, then the type is annotated. If it has ElementType_FIELD as a target, then the field is annotated. Thus, the situation is similar to that just described for methods, and no ambiguity exists. The same mechanism also disambiguates annotations on local variables.

Next, notice how this (the receiver) is annotated here:

 

public int f(@TypeAnno TypeAnnoDemo<T> this, int x) {

 

Here, this is specified as the first parameter and is of type TypeAnnoDemo (which is the class of which f( ) is a member). As explained, beginning with JDK 8, an instance method declaration can explicitly specify the this parameter for the sake of applying a type annotation.

Finally, look at how array levels are annotated by the following statement:

 

String @MaxLen(10) [] @NotZeroLen [] w;

 

In this declaration, @MaxLen annotates the type of the first level and @NotZeroLen annotates the type of the second level. In this declaration

 

@TypeAnno Integer[] vec;

 

the element type Integer is annotated.


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.