Annotations are a metaprogramming facility introduced J2SE 5.0. They allow us to mark code with defined tags. Some developers think that the Java compiler understands the tag and work accordingly. This is not right. The tags actually have no meaning to the Java compiler or runtime itself. There are tools that can interpret these tags. For instance: IDEs, testing tools, profiling tools, and code-generation tools understands these tags.


Why use annotations?

Metadata is beneficial for documentation, compiler checking, and code analysis.
One can use metadata to indicate if methods are dependent on other methods, if they are incomplete, if a certain class must reference another class, and so on. You might be thinking that this can also be done using Javadoc since it provides a fairly easy and robust way to document code. Yes, this is correct but the truth is, there are other benefits associated with metadata.

Metadata is used by the compiler to perform some basic compile-time checking. For example there is a override annotation that lets you specify that a method overrides another method from a superclass. At this, the Java compiler will ensure that the behavior you indicate in your metadata actually happens at a code level as well. This might seem trivial to you but the fact is that many developers have spent long nights trying to discover why their code is not working and later realizing that a method has a parameter wrong, which means that the method is not overriding a method from a superclass.
Another great feature of using annotation is code analysis. A good tool like XDoclet can manage all of these dependencies, ensuring that classes that have no code-level connection, but do have a logic-level tie-in, stay in sync. In this case, metadata is really useful.


Annotations and Annotation type

I will differentiate these through an example. You can define a single class and you will always have only one version of the class in the JVM. But you can have 5 or 10 or even more instances of that class in use at any given time. The same is true of annotation types and annotations. An annotation type is same as class, and an annotation is same as instance of that class.

Annotations in Tiger

Annotations are indicated by an "at" sign (@), followed by the annotation name. When data is required, you specify the data in name=value pairs.


Categories of annotations

There are three categories of annotations:

Marker annotations

There annotations have no variables. These are identified by name, with no additional data supplied. For example:
Java Code:
1
@SingleValueAnnotation
Single-value annotations

These are similar to markers, but also provide a single piece of data. You can only provide a single bit of data with these. For example:
Java Code:
1
@SingleValueAnnotation("my data")
Full annotations

There have multiple data members. Annotations of this type won’t look quite so much like a normal Java method:

Java Code:
1
@FullAnnotation(var1="data value 1", var2="data value 2", var3="data value 3")
In addition to supplying values to annotations through the default syntax, you can use name-value pairs when you need to pass in more than one value. You can also supply arrays of values to annotation variables, through curly braces. Following snippet shows an array of values in an annotation.

Java Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@TODOItems({    // Curly braces indicate an array of values is being supplied
  @TODO(
    severity=TODO.CRITICAL,
    item="Add functionality to calculate the mean of the student's grades",
    assignedTo="Brett McLaughlin"
  ),
  @TODO(
    severity=TODO.IMPOTANT,
    item="Print usage message to screen if no command-line flags specified",
    assignedTo="Brett McLaughlin"
  ),
  @TODO(
    severity=TODO.LOW,
    item="Roll a new website page with this class's new features",
    assignedTo="Jason Hunter"
  )
})
The above example might look complex to you, but it’s rather simple. Let me explain it.

TODOItems and TODO are custom annotations. The TODOItems annotation type has a single variable that takes a value. Thing to note is that the single value is an array which contains three TODO annotations.

TODO annotation is multivalued annotation and commas separates the values within each annotation. Also comma is used to separate the value within a single array.

The Override annotation

Tiger provides many built-in annotation types and Override is one of them. Override should be used only on methods to indicate that the annotated method is overriding a method in a superclass. An example is presented below:

Java Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.domian.a.test;
 
public class OverrideTester {
 
  public OverrideTester() { }
 
  @Override
  public String toString() {
    return super.toString() + " [Override Tester Implementation]";
  }
 
  @Override
  public int hashCode() {
    return toString().hashCode();
  }
}
In the above example, the @Override annotation annotates two methods toString() and hashCode(). Annotations indicate that these methods are override versions of the methods from the OverrideTester class's superclass which is java.lang.Object.

The following example shows the advantage is using annotations.

Java Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.domian.a.test;
 
public class OverrideTester {
 
  public OverrideTester() { }
 
  @Override
  public String toString() {
    return super.toString() + " [Override Tester Implementation]";
  }
 
  @Override
public int hasCode() {
    return toString().hashCode();
  }
}
In the example, hashCode() is mistyped as hasCode(). The annotation indicates that hasCode() should override a method. But in compilation, javac will realize that the superclass (again, java.lang.Object) has no method named hasCode() to override. As a result, the compiler gives you an error.


The Deprecated annotation

Deprecated is a standard annotation and it’s a marker annotation. We use Deprecated to annotate a method that shouldn't be used anymore. Note: Deprecated should be placed on the same line as the method being deprecated.

Java Code:
1
2
3
4
5
6
7
8
9
10
11
12
package com.domian.a.test;
 
public class DeprecatedClass {
 
  @Deprecated public void doSomething() {
    // some code
  }
 
  public void doSomethingElse() {
    // This method presumably does what doSomething() does, but better
  }
}
The SuppressWarnings annotation

SuppressWarnings is also a standard annotation. It is used to suppress warnings. For instance, Java generics make new type-safe operations possible. However, because of generics, the compiler now throws warnings when collections are used without type safety.

SupressWarnings is the single valued annotation. The variable can be an array of values, each of which indicates a specific type of warning to suppress. Take a look at the following example, which is some code that normally generates an error in Tiger.

Java Code:
1
2
3
4
public void nonGenericsMethod() {
List wordList = new ArrayList();   
wordList.add("foo");
}
Java compiler will show warning for the code presented above. To get rid of warning, use SuppressWarnings annotation.

Java Code:
1
2
3
4
5
@SuppressWarnings(value={"unchecked"})
public void nonGenericsMethod() {
List wordList = new ArrayList();
wordList.add("foo");
}
As mentioned above, you may supply multiple warning types (comma separated) as value for SuppressWarnings annotation.

Java Code:
1
@SuppressWarnings(value={"unchecked", "fallthrough"})

Using annotations with Hibernate

Hibernate requires metadata that governs the transformation of data from one representation to the other. You can use JDK 5.0 annotations for object/relational mapping with Hibernate 3.2.

The Hibernate Annotations package includes:

- Standardized Java Persistence and EJB 3.0 (JSR 220) object/relational mapping annotations
- Hibernate-specific extension annotations for performance optimization and special mappings
Java Code:
1
2
3
4
5
6
7
8
@Lob
public String getFullText() {
return fullText;
}
@Lob
public byte[] getFullCode() {
return fullCode;
}
In the above example, we have used @Lob which indicates that the property should be persisted in a Blob or a Clob depending on the property type:
java.sql.Clob, Character[], char[] and java.lang.String will be persisted in a Clob. java.sql.Blob,
Byte[], byte[] and serializable type will be persisted in a Blob.

The columns used for a property mapping can be defined using the @Column annotation. For example:

Java Code:
1
2
3
4
5
@Entity
public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
The name property is mapped to the flight_name column, which is not nullable, has a length of 50 and is not updatable.

Using annotations, it is possible to declare an embedded component inside an entity and even override its column mapping. Component classes have to be annotated at the class level with the @Embeddable annotation. It is possible to override the column mapping of an embedded object for a particular entity using the @Embedded and @AttributeOverride annotation in the associated property:

Java Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Entity
public class Person implements Serializable {
// Persistent component using defaults
Address homeAddress;
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),
@AttributeOverride(name="name", column = @Column(name="bornCountryName") )
} )
Country bornIn;
...
}
@Embeddable
public class Address implements Serializable {
String city;
Country nationality; //no overriding here
}
@Embeddable
public class Country implements Serializable {
private String iso2;
@Column(name="countryName") private String name;
public String getIso2() { return iso2; }
public void setIso2(String iso2) { this.iso2 = iso2; }
public String getName() { return name; }
...
}

Using annotations with Java Persistence API

The Java Persistence API heavily depends on the metadata annotations. The API consists of:

Java Persistence API
The Java Persistence query language
Metadata annotations

An entity is a persistence object. It is coded as a POJO, and marked as an entity with the @Entity (javax.persistence.Entity) annotation. By default, all properties/fields are persisted into the datastore, except those marked with the @Transient annotation.
Following example shows few annotations that are used with entity beans.
Java Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package university;
 
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.Id;
 
@Entity
@Table (name="Dept")
public class Department implements Serializable {
@Id
@Column(name="id", nullable=false)
private String deptId;
@Column(name="name")
private String deptName;
private String location;
 
public void setDeptId(String deptId) {
this.deptId = deptId;
}
 
public String getDeptId() {
return this.deptId;
}
 
public void setDeptName(String deptName) {
this.deptName = deptName;
}
 
public String getDeptName() {
return this.deptName;
}
 
public void setLocation(String location) {
this.location = location;
}
 
public String getLocation() {
return location;
}
..................
..................
}
We used @Entity to mark a simple POJO-based class as an entity. The table represented by the entity is denoted using the @Table annotation. Remember, if the entity name is the same as that of the table name, @Table is not required. The @Column annotation specifies the database column that corresponds to the property/field name and if a field is not annotated with @Column, the column name is assumed to be the same as that of the field name in the persistence storage. @Id annotation is used to make a field as primary key for the entity class.

The relationship multiplicities among entities may exist and there are annotations for that as well.

If each entity instance is related to a single instance of another entity, then one-to-one relation exists and

@OneToOne(javax.persistence.OneToOne) is used.
If an entity instance can be related to multiple instances of other entities, then one-to-many relation exists and

@OneToMany(javax.persistence.OneToMany) is used.
If multiple instances of an entity can be related to a single instance of the other entity,then many-to-one relation exist and

@ManyToOne(javax.persistence.ManyToOne) is used.

If entity instances can be related to multiple instances of each other, then we say that many-to-many relation exist and @ManyToMany (javax.persistence.ManyToMany) is used.

Conclusion

Annotations are pretty easy to understand and use. They help is documentation, compiler checking, and code analysis. You can introduce custom annotations for your own applications.