You are on page 1of 27

Taming Tiger, An introduction to Java 1.5 ..........................................................................

2
Autoboxing and unboxing................................................................................................3
The enhanced for loop.....................................................................................................3
Variable method arguments and printf.............................................................................4
Enumerations...................................................................................................................5
Static imports...................................................................................................................8
Conclusion.......................................................................................................................9
Taming Tiger, Understanding generics................................................................................9
Why generics?................................................................................................................10
Enter Java generics........................................................................................................10
Diving into generics.......................................................................................................11
Using wildcards.............................................................................................................12
Roll your own parameterized type.................................................................................14
Generic methods............................................................................................................15
Conclusion.....................................................................................................................15
Taming Tiger, Java annotations .........................................................................................15
What is metadata?..........................................................................................................16
Built-in annotations........................................................................................................17
java.lang.Overrides....................................................................................................17
java.lang.annotation.Documented..............................................................................18
java.lang.annotation.Deprecated................................................................................18
java.lang.annotation.Inherited....................................................................................18
java.lang.annotation.Retention...................................................................................19
java.lang.annotation.Target........................................................................................19
Create a custom annotation............................................................................................20
Conclusion.....................................................................................................................27

Page 1 of 27
Taming Tiger, An introduction to Java 1.5
By Tarak Modi, JavaWorld.com, 04/26/04
Welcome to the first of a three-part series on Sun Microsystems' latest release of the Java
2 Platform, Standard Edition (J2SE). J2SE 1.5—code-named Tiger—is the most
significant revision to the Java language since its origination. With version 1.5, Java has
been extended with several new features, and my goal with this series is to introduce you
to the most important features.
Each article presents a detailed discussion of a handful of distinct features and provides
practical examples of how they can be used in the real world. Part 1 is a quick
introduction to J2SE 1.5 and covers many new additions. Part 2 will be devoted to
generics, Java's counterpart to templates in C++ and a similar facility (also called
generics) in C# 2.0. Part 3 focuses exclusively on J2SE 1.5's new metadata facility called
annotations, which allow programmers to decorate Java code with their own attributes.
These custom attributes can be used for code documentation, code generation, and,
during runtime, to provide special services such as enhanced business-level security or
special business logic.
Read the whole series: "Taming Tiger," Tarak Modi (JavaWorld):
•Part 1: An introduction to Java 1.5
•Part 2: Understanding generics
•Part 3: Decorate your code with Java annotations

As I mentioned before, J2SE 1.5 represents the largest overhaul of Java since its
inception and contains many new and exciting features. The additions provide developers
with a host of new capabilities. For example, the addition of enumerations and generics
provides Java with compile-time type safety. That means programming or logic errors are
caught during program compilation instead of during runtime. This may not seem to be a
great benefit on the surface, especially for smaller projects. However, as your code's
complexity increases and your project grows larger, it becomes more likely that parts of a
program may not have been unit tested (I know this never happens on your project!), and
after three months of smooth sailing in production, you answer a call at 2 a.m. that
informs you of a java.lang.ClassCastException. Compile-time type safety catches all
these errors during program compilation.

Page 2 of 27
In this article, I discuss some of the new language features introduced in J2SE 1.5,
including autoboxing and unboxing of primitives, the enhanced for loop, variable
method arguments (varargs), the much anticipated enumerations, static imports, and the
new formatted input method printf (a concept borrowed from C and C++).
All of these features are part of Java Specification Request 201. Without further ado, let's
start with the first feature—autoboxing and unboxing.

Autoboxing and unboxing


Consider the following code fragment:
// A list that stores numbers
// Generics will make this obvious...
List numbers = new ArrayList();
numbers.add(89);

Prior to version 1.5, this code would not compile since ArrayList's add method expects
an object. To make it work, you must modify the second line as follows:
numbers.add(new Integer(89));

That seems unnecessary and, with J2SE 1.5, it is. With J2SE 1.5, the compiler
automatically adds the code to convert the integer value (such as 89) into the proper class
(Integer in our example). That is called boxing. The opposite process of converting an
object (such as of type Integer) into a value (such as an int) is called unboxing. Boxing
and unboxing eliminate the (frequent and often burdensome) need to explicitly convert
data of primitive type to reference and vice versa. Such explicit required conversions are
verbose and induce clutter in the program text. The following table shows the rules for
boxing and unboxing.
Primitive Reference
type type
boolean Boolean
byte Byte
double Double
short Short
int Integer
long Long
float Float

The enhanced for loop


A common programming task is iterating over the elements of a collection or an array. An
example is shown in the code fragment below:

Page 3 of 27
// numbers is a list of Numbers
for (Iterator it = numbers.iterator(); it.hasNext(); )
{
Integer number = (Integer) it.next();

// Do something with the number...


}

The above code fragment, although effective, is cumbersome. Version 1.5 enhances the
for statement to accept a more evolved syntax (the previous code still works) such as:
for(Integer number: numbers)
{
// Do something with the number...
}

The above code actually compiles into code similar to the first code fragment. The
enhanced for loop also works with arrays. Personally, I would have preferred a new
keyword such as "foreach." This is actually a FAQ, and Sun has responded to it by saying
that creating a new keyword would be too "costly." Sun has, however, added keywords
before, such as the assert keyword in 1.4 and the new enum keyword (we'll see this one
shortly) in 1.5. So, I am not too convinced by this answer. Finally, remember that if you
wish to modify the collection (such as remove elements) while traversing it, you cannot
use the enhanced for loop.

Variable method arguments and printf


Often, having a method that can operate on a variable number of parameters is
convenient. A commonly accepted way of accomplishing that is to define the method so it
accepts an array or collection of objects. An example code fragment is shown below:
// An example method that takes a variable number of parameters
int sum(Integer[] numbers)
{
int mysum = 0;
for(int i: numbers)
mysum += i;
return mysum;
}
// Code fragment that calls the sum method
sum(new Integer[] {12,13,20});

While this proves effective in simulating a method that can take a variable number of
parameters, it seems clunky and involves slightly more (unnecessary) work by the
programmer. Fortunately, that is no longer required in version 1.5 thanks to the new
variable arguments (varargs) feature. Let's rewrite the above code fragments using
varargs.
// An example method that takes a variable number of parameters
int sum(Integer... numbers)

Page 4 of 27
{
int mysum = 0;
for(int i: numbers)
mysum += i;
return mysum;
}
// Code fragment that calls the sum method
sum(12,13,20);

Note the change in the method signature. Instead of explicitly taking an array of Integer
objects, we inform the compiler that a variable number of Integer objects will be passed
using an ellipsis (...). Note that the actual code inside the method does not change.
Finally, notice how much cleaner (and simpler) the method call becomes. A perfect
example of where this feature has been used by version 1.5 itself is in the implementation
of the C-style formatted output method printf:

// Using System.out.println and System.out.printf


int x = 5;
int y = 6;
int sum = x + y;
// Pre 1.5 print statement
System.out.println(x + " + " + y + " = " + sum);
// Using 1.5 printf
System.out.printf("%d + %d = %d\n", x, y, sum);

Both statements print the same line to the console—5 + 6 = 11. You can do a lot more
with the extremely powerful and versatile printf method. Look at the following code
fragment:
// Demonstrating printf versatility
System.out.printf("%02d + %02d = %02d\n", x, y, sum);

This line of code ensures that each number prints as two digits and, if the number is a
single digit, then a zero (0) precedes it. So the output on the console now looks as
follows: 05 + 06 = 11.

Enumerations
Consider the following class:
public Class Color
{
public static int Red = 1;
public static int White = 2;
public static int Blue = 3;
}
You could use the class as shown in the following code fragment:
int myColor = Color.Red;

Page 5 of 27
But, what is to prevent someone from doing the following?
int myColor = 999;

Obviously the compiler will not (and cannot) catch this error, which quite possibly will
lead to a runtime exception.
Here's an alternative implementation of the Color class that solves the problem
mentioned above:
// The new Color class
public class Color
{
// Color value;
int _color;

// Constructor
protected Color (int color)
{
_color = color;
}
public static final int _Red = 1;
public static final int _White = 2;
public static final int _Blue = 3;
public static final Color Red = new Color(_Red);
public static final Color White = new Color(_White);
public static final Color Blue = new Color(_Blue);
}

And here is how you would use it:


Color myColor = Color.Red;

The above implementation ensures that invalid colors cannot be specified. However, we
did a lot of work and our solution remains limited. J2SE 1.5 provides a better solution
with its addition of enumerations; a concept C and C++ programmers are already familiar
with. Let's rewrite the Color class using enumerations:
// The color enumeration
public enum Color
{
Red,
White,
Blue
}
Notice the usage of the keyword enum instead of class. Also, notice how simple the
implementation has become. Here's how you could use the Color enumeration in your
code:
// Using the Color enumeration
Color myColor = Color.Red;

Page 6 of 27
The usage is the same as the usage of our second example (above), but the enumeration is
much more capable than our simple Color class implementation. For example, all
enumerations have a static method called values() that returns all the enumeration
values in a collection as shown in the code fragment below:
// Cycling through the values of an enumeration
for (Color c : Color.values())
System.out.println(c);

The above code fragment produces the following output:


Red
White
Blue

All enumerations also have another static method called valueOf() that returns the
enumeration constant corresponding to a string parameter. For example
Color.valueOf("Red") returns Color.Red.
In addition, each enumeration has several useful instance methods. The name() method
returns the enumeration constant's name, exactly as declared in its declaration. For
example, Color.Red.name() returns Red. The toString() method returns the same
value as name(), but can be overridden to provide a better and more friendlier or
meaningful (and even internationalized) name. The ordinal() method returns the zero-
based position of the declared constant. For example, Color.White.ordinal() returns
1. Finally, the compareTo() method compares the current enumeration object (i.e., this)
with the specified enumeration object and returns a negative integer, zero, or a positive
integer, depending on whether the current enumeration object is less than, equal to, or
greater than the specified object, respectively. The comparison is based on the ordinal of
the declared enumeration constants. For example,
Color.Red.compareTo(Color.White) will return -1.
Enumerations can have constructors and methods as well. For example, let's say I wanted
to provide a friendlier name than offered by the default toString() implementation.
Here's an example that accomplishes that:
// The revised color enumeration
public enum Color
{
Red("Red (#FF0000)"),
White("White (#000000)"),
Blue("Blue (#0000FF)"),
Green("Green (#00FF00)")
{
public boolean isInUSFlag()
{
return false;

Page 7 of 27
}
};

private String newName;

Color(String s)
{
newName = s;
}
public String toString()
{
return newName;
}
public boolean isInUSFlag()
{
return true;
}
}

Note that I also added a new color, Green, and a new method called isInUSFlag() that
by default returns true. Since green is not in the US flag, I override the isInUSFlag()
method when I declare the constant Green and return false from the overridden method
implementation.

Static imports
The last new language feature explained in this article is the static import. Static imports
are yet another convenience feature added to version 1.5 that extends the way imports
work in Java. For example, consider the code fragment shown below that calls the static
ceil() method on the java.lang.Math class
// x is a number of type double such as 5.345
double y = Math.ceil(x);

With static imports in 1.5, you can ask the Java compiler to import only a class's static
portions, as shown, for example, in the rewritten code fragment below:
// Import declaration at the top of the class along with the other
imports
import static java.lang.Math.ceil;
// And then somewhere in the code...
// x is a number of type double such as 5.345
double y = ceil(x);

In the above fragment, I used the new static import feature to import the static method
ceil() from the Math class. Now when I call the method, I don't have to qualify it with
Math. If I wanted to use multiple static methods from the Math class, I could import them
individually or all at once as shown below:

Page 8 of 27
// Import all static methods from Math
import static java.lang.Math.*;

This also applies to any constants declared within Math, such as E and PI. With the above
declaration, I can use these constants as if they were declared locally within my class (or
superclass).
Finally, static imports can be used with enumerations as well. For example, consider the
following enumeration definition:
package myEnumerations;
public enum Color
{
Red,
White,
Blue
}

You may access this enumeration's values in (at least) one of the following two ways:
Import myEnumerations.Color and reference Color.Red in your code
Statically import myEnumerations.Color.* and reference Red in your code

Conclusion
This article has provided a quick introduction to a few of the new language features in
J2SE 1.5. To be completely honest, none of these features introduce any groundbreaking
or previously impossible functionality into the Java language. As we saw throughout the
article, there were always workarounds. What the new features do provide are reduced
code complexity, enhanced readability, and much better compile-time type safety.
However, if I had to choose one feature that most impresses me, I would select
enumerations. In my opinion, Sun and the Java team have done an excellent job with its
implementation of enumerations by making it extremely close to a first-class class within
Java, thus enabling enumerations to have constructors, methods, and method overriding.
In Part 2 this series, I will go into detail about the newly introduced templating feature in
J2SE 1.5 called generics. Generics are very exciting and allow you to take compile-time
type safety to yet another level. Also, unlike the features discussed in this article, generics
do introduce groundbreaking functionality in Java.

Taming Tiger, Understanding generics


By Tarak Modi, JavaWorld.com, 06/07/04

Page 9 of 27
Welcome to the second part of this three-part series on Sun Microsystems' latest release
of the Java 2 Platform, Standard Edition (J2SE). To refresh your memory, Part 1 was a
quick introduction to J2SE 1.5 and covered many new additions to the Java language:
auto-boxing and -unboxing of primitives, enumerations, static imports, the enhanced
"for" loop, variable method arguments, and the newly formatted input method. I devote
Part 2 entirely to generics, Java's counterpart to templates in C++ and a similar facility
(also called generics) in C# 2.0.
Read the whole series: "Taming Tiger," Tarak Modi (JavaWorld):
•Part 1: An introduction to Java 1.5
•Part 2: Understanding generics
•Part 3: Decorate your code with Java annotations

Why generics?
To understand the problem that generics (short for generic types) solve, let's look at the
following code fragment:
// Declare Class A
class A
{
}
// Declare Class B
class B
{
}
// Somewhere in the program create a Vector
Vector v = new Vector();
// Add an object of type A
v.add(new A());
// And sometime later get the object back
B b = (B) v.get(0);

The above code will compile fine but will throw a runtime exception
(java.lang.ClassCastException) when you execute it. This is obviously a serious
problem and should be caught as early as possible. Preferably, the above code fragment
should not even compile.

Enter Java generics


Now, let's rewrite the above code fragment using generics:
// Class A and B declared previously
// Somewhere in the program create a Vector
Vector<A> v = new Vector<A>();
// Add an object of type A
v.add(new A());
// And sometime later get the object back

Page 10 of 27
B b = (B) v.get(0);

That looks similar to the first fragment, except for the code in the angle brackets. The
angle brackets are the syntax for providing type parameters to parameterized types. I talk
more about both parameterized types and type parameters in this article's later sections.
Even though the code change is minimal, its impact is far from small. This code fragment
will not compile. Compiling this program with J2SE 1.5 (remember to use -source 1.5)
will give you the following error:
inconvertible types
found : A
required: B
B b = (B) v.get(0);

In plain English, the compiler tells us that to be able to use the line B b = (B)
v.get(0); legally, the vector must be parameterized (in the declaration) to accept objects
of type B.

Diving into generics


Generics were first proposed for Java in early 2001, and (after three long years) they have
finally made their way into J2SE 1.5. As you saw above, generics provide a giant leap
into solving a long-standing problem in Java; implementing compile-time type safety.
One of the first places you will encounter generics in J2SE 1.5 (and in C++ and C#) are
in the collection classes, such as the Vector class we used above. Such classes are called
parameterized types. If a type is parameterized, then its declaration (i.e., when you
declare an instance of that type) can optionally take a type with which to parameterize it.
For example, we already know that Vector is a parameterized type, and hence, the
declaration of v (in the example above) is as follows:
Vector<A> v = new Vector<A>();

The type is optional to maintain backwards compatibility with legacy code. If the type
were mandatory, then any code with a declaration such as Vector v = new Vector();
would no longer compile. Once a parameterized type is declared, using it is no different
than a regular (pre 1.5) nonparameterized type.
There are subtleties to keep in mind while using generics. Consider the following
seemingly harmless code fragment:
// Somewhere in the program create a Vector
Vector<String> v2 = new Vector<String>();
Vector<Object> v = v2;

Page 11 of 27
Compile the code and run it. What? It won't compile. Even though Object is the base
class of all classes including String, you cannot assign a vector of type String to a
vector of type Object. The converse is also true and is more intuitive than the former.
The reason such rules exist are to ensure compile-time type safety and to prevent errors
such as the following:
// Somewhere in the program create a Vector
Vector<String> v2 = new Vector<String>();
Vector<Object> v = v2;
v.add(new A());
String s = v2.get(0);

Assuming that J2SE 1.5 relaxed its assignment rules, the above code would compile, but
would result in a runtime exception when the line String s = v2.get(0) executes.

Using wildcards
To understand what wildcards are and why we need them, consider the following code
fragment:
// Declare a Mammal class
abstract class Mammal
{
abstract public void eat();
}
// Declare a Dog class
class Dog extends Mammal
{
public void eat()
{
System.out.println("Dog is eating.");
}
}
// Declare a Cat class
class Cat extends Mammal
{
public void eat()
{
System.out.println("Cat is eating.");
}
}
// Somewhere in the code create a vector of dogs and cats
Vector<Dog> dogs = new Vector<Dog>();
dogs.add(new Dog());
dogs.add(new Dog());
Vector<Cat> cats = new Vector<Cat>();
cats.add(new Cat());
cats.add(new Cat());
cats.add(new Cat());
// Now make all the dogs and cats eat
Vector<Mammal> pets = dogs;
for(Mammal pet: pets)
{

Page 12 of 27
pet.eat();
}
pets = cats;
for(Mammal pet: pets)
{
pet.eat();
}

By now we already know that this code when compiled will produce the following errors:
incompatible types
found : java.util.Vector<Dog>
required: java.util.Vector<Mammal>
Vector<Mammal> pets = dogs;
^
incompatible types
found : java.util.Vector<Cat>
required: java.util.Vector<Mammal>
pets = cats;

But what if I wanted to make this code work? After all, it is legitimate—albeit contrived
—code. The answer is simple and involves using wildcards. Here's the one line change
that will appease the Java compiler:
Vector<? extends Mammal> pets = dogs;

Now after you successfully compile and run the code, you will receive the following
output:
Dog is eating.
Dog is eating.
Cat is eating.
Cat is eating.
Cat is eating.

So how did that work? The ? is a wildcard character that, along with the extends
keyword, declares pets as a Vector of any type that derives from Mammal. That differs
from the line we wrote before that declared pets as a Vector that could only contain
objects of the direct type Mammal. To accept a Vector of any kind of object you could use
the following declaration:
Vector<?>

Note: That code differs from Vector<Object> for the same reasons discussed above.

Page 13 of 27
Roll your own parameterized type
So far we've used the built-in parameterized types in Java. Now let's look at the other side
of the coin—creating parameterized types. It's fairly easy to create your own
parameterized types. As an example, let's create a new class called Mammals that can store
any Mammal:
class Mammals<T extends Mammal>
{
private Vector<T> items = new Vector<T>();
void add(T item)
{
items.add(item);
}
T get(int index)
{
return items.get(index);
}
}

Now you can use this new class in your code, as shown in the following fragment:
// A list of dogs
Mammals<Dog> dogs2 = new Mammals<Dog>();
dogs2.add(new Dog());
dogs2.add(new Dog());
// A list of cats
Mammals<Cat> cats2 = new Mammals<Cat>();
cats2.add(new Cat());
cats2.add(new Cat());
cats2.add(new Cat());
// Assign the dogs to a generic list of mammals
Mammals<? extends Mammal> mammals = dogs2;

To support iterating through the Mammals list, I modify the class as follows:
class Mammals<E extends Mammal> extends Vector<E>
{
}

No, I did not forget the code in the class. I extended the Vector class and inherited all the
required functionality (Isn't that what object-oriented programming is all about?). Now
we can iterate through the list, as shown below:
Mammals<? extends Mammal> mammals = dogs2;
for (Mammal m: mammals)
{
m.eat();
}
mammals = cats2;

Page 14 of 27
for (Mammal m: mammals)
{
m.eat();
}

Generic methods
J2SE 1.5 supports generic methods. To see how such methods can be useful, let's say you
wanted to create a method that took an array of Mammals and put them in a Mammals list.
Here's one way of accomplishing that:
// Mammals class from earlier example
class Mammals<E extends Mammal> extends Vector<E>
{
}
// A generic method
static <T extends Mammal> void arrayToList(T[] array, Mammals<T> list)
{
for(T item: array)
list.add(item);
}

Generic methods prove especially useful when new functionality to an existing


parameterized class is needed and it is not possible to extend the class, or when a group
of such methods are provided as common utility methods.

Conclusion
I talked at length about generics in this article. Generics are a strong addition to Java and
provide a level compile-time type safety not possible in previous versions of the
language. They are also used extensively within J2SE 1.5. In addition, not surprisingly,
Reflection in J2SE 1.5 has also been extended to support reflecting on generic types.
However, those details reach beyond the scope of this introductory article. Perhaps, if
there is enough interest, I will write an article exclusively on using Reflection on generics
(and a potential weakness with Java's generics implementation called erasure).
In the third and final part of this series, I will go into details about the newly introduced
metadata feature in J2SE 1.5 called annotations. Annotations allow programmers to
decorate Java code with their own attributes that can be used for code documentation,
code generation, and, during runtime, for providing special services such as enhanced
business-level security or special business logic.

Taming Tiger, Java annotations


By Tarak Modi, JavaWorld.com, 07/19/04

Page 15 of 27
Welcome to the third and final part of this three-part series on Sun Microsystems' latest
release of the Java 2 Platform, Standard Edition (J2SE). As I mentioned in Part 1, J2SE 5
(also called Tiger) is the most significant revision to the Java language since its original
inception and has extended Java with several new features. To refresh your memory, Part
1 provided a brief introduction to J2SE 5 and covered many new additions to the Java
language. Part 2 was devoted entirely to generics, which are Java's counterpart to
templates in C++ and a similar facility (also called generics) in C# 2.0. In this final
installment, I focus exclusively on the newly introduced metadata feature in J2SE 5
called annotations. Annotations allow programmers to decorate Java code with their own
attributes. These attributes can be used for code documentation, code generation, and,
even during runtime, for providing special services such as enhanced business-level
security or special business logic.
Read the whole series: "Taming Tiger," Tarak Modi (JavaWorld):
•Part 1: An introduction to Java 1.5
•Part 2: Understanding generics
•Part 3: Decorate your code with Java annotations

What is metadata?
If you look up the definition of metadata, the dictionary will tell you it's data about data.
The same definition applies to metadata in Java as well. Metadata is not a completely
foreign concept to Java. Java has always supported a (very) limited implementation of
metadata via its javadoc tags. An example code fragment follows:
/**
* @author Tarak Modi
*
*/
public final class AnnotationsTest
{
}

In the above code fragment, the @author tag is an example of metadata for the
AnnotationsTest class. In other words, AnnotationsTest is annotated or decorated by
the author metadata. Currently, this metadata is only used by tools such as javadoc and
is not even available during runtime. XDoclet, an open source project, is another example
that uses javadoc-style metadata tags to annotate and generate code based on the
metadata.
With J2SE 5, the Java team has taken Java's metadata capability to a new level. The
concept is formally called annotations. In addition to the built-in annotations included in
Version 5, you can also use custom annotations that you define to decorate types (such as
classes, interfaces, and enums), methods and constructors, and fields. You can control the
availability of these annotations as well. For example, some annotations may be available

Page 16 of 27
only in the source code; others may be available in the compiled class files as well. Some
annotations may even be available during runtime in the JVM.

Built-in annotations
J2SE 5 comes with six prebuilt annotations. Each annotation is described below. The
annotation's actual definition is also provided in the reference's description. Two points to
note from the definitions are their simplicity and that the annotations themselves are
annotated. Later in this article, I walk you through a complete example of building and
using your own annotation; during that discussion, I explain the declaration's syntax.
The following are the six built-in annotations in J2SE 5:

java.lang.Overrides
@Target(ElementType.METHOD)
public @interface Overrides
{
}

This annotation is used to indicate that a method declaration in the current class is
intended to override a method declaration in its (direct or indirect) superclass. If a method
is decorated with this annotation type but does not override a superclass method, then the
compiler will generate an error message. As an example, consider the following code
fragment:
class A
{
@Overrides
public String toString(int i)
{
return "";
}
}

Compiling this code fragment produces the following error during compilation:
method does not override a method from its superclass
@Overrides
^

To fix the error, change the method signature from public String toString(int i) to
public String toString(). Using this annotation is a good way to catch inadvertent
programming errors during compilation, where you think you are overriding a method,
but in reality you are creating a new one.

Page 17 of 27
java.lang.annotation.Documented
@Documented
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented
{
}

This annotation indicates that the annotation to which this is applied is to be documented
by javadoc and similar tools. Note that this annotation is just a hint, and a tool can ignore
the annotation if it desires to do so.

java.lang.annotation.Deprecated
@Documented
@Retention(RetentionPolicy.SOURCE)
public @interface Deprecated
{
}

This annotation provides a hint to the Java compiler to warn users if they use the class,
method, or field annotated with this annotation. Typically, programmers are discouraged
from using a deprecated method (or class), because either it is dangerous or a better
alternative exists.
As an example of when you could use the Deprecated annotation, consider a scenario
where you have implemented a sort() method on a class used by many other developers
on different projects. Later you discover a shortcoming in the code and not only have to
rewrite the function, but also change its signature. You can't remove the old sort()
method because other programs rely on it. What you can do is annotate it as being
deprecated so when developers work on and compile their code, they will be advised to
use the new sort() method instead of the old one.

java.lang.annotation.Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited
{
}

The best way to understand this annotation is through an example. Let's say you created
your own annotation called Serializable. A developer would use your annotation if his
class implemented the Serializable interface. If the developer created any new classes
that derived from his original class, then marking those classes as Serializable would
make sense. To force that design principal, you decorate your Serializable annotation

Page 18 of 27
with the Inherited annotation. Stated more generally, if an annotation A is decorated
with the Inherited annotation, then all subclasses of a class decorated by the annotation
A will automatically inherit the annotation A as well.

java.lang.annotation.Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention
{
RetentionPolicy value();
}

The Retention annotation takes a single parameter that determines the decorated
annotation's availability. The values of this parameter are:
RetentionPolicy.SOURCE: The decorated annotation is available at the source code
level only
RetentionPolicy.CLASS: The decorated annotation is available in the source code
and compiled class file, but is not loaded into the JVM at runtime
RetentionPolicy.RUNTIME: The decorated annotation is available in the source
code, the compiled class file, and is also loaded into the JVM at runtime
By default, all annotations are available at the source level only.

java.lang.annotation.Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target
{
ElementType[] value();
}

This annotation is used to indicate the type of program element (such as a class, method,
or field) to which the declared annotation is applicable. If the Target annotation is not
present on an annotation type declaration, then the declared type may be used on any
program element. If the Target annotation is present, then the compiler will enforce the
specified usage restriction. Legal values for the Target annotation are contained in the
java.lang.annotation.ElementType enumeration. An annotation can be applied to
any combination of a class, a method, a field, a package declaration, a constructor, a
parameter, a local variable, and another annotation.

Page 19 of 27
Create a custom annotation

It's easy to create and then use a custom annotation. As you have already seen above, the
declaration of an annotation is straightforward. In this section, I walk you through a
complete exercise of creating, applying, and dynamically querying a custom annotation
during runtime.
The custom annotation
Let's create an annotation that can be applied to classes and methods and enforces role-
based security. The declaration is shown below:
@Documented
@Target({METHOD,TYPE})
@Retention(RUNTIME)
public @interface SecurityPermission
{
String[] value();
}

We can glean the following information from the declaration:


The annotation is to be documented by tools such as javadoc.
The annotation can be applied only to methods and types such as classes.
The annotation should be available during runtime.
The annotation is called SecurityPermission.
The annotation has one member called value and is an array of string. Note that we
don't declare the actual member, only an accessor method to retrieve it.

The next step is declaring a class that uses our new annotation. An example is shown
below:
@SecurityPermission("All")
public class AnnotationsTest
{
public AnnotationsTest()
{
SecurityBlanket.checkPermission();
}
@SecurityPermission("All")
public void unRestrictedMethod(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from unRestrictedMethod: " +
message);
}
@SecurityPermission("None")

Page 20 of 27
public void fullyRestrictedMethod(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from fullyRestrictedMethod: " +
message);
}
@SecurityPermission("Manager")
public void partiallyRestrictedMethod(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from partiallyRestrictedMethod: " +
message);
}
@SecurityPermission({"Manager","HR"})
public void partiallyRestrictedMethod2(String message)
{
SecurityBlanket.checkPermission();
System.out.println("Message from partiallyRestrictedMethod2: "
+ message);
}
}

As you can see, AnnotationsTest is a class just like any other with special javadoc-like
tags (@SecurityPermission) above the class and method declarations. This is how you
apply the SecurityPermission annotation. Note that this is also the same way you apply
the built-in annotations to Java code, such as, for example, to the SecurityPermission
annotation declaration. Next, look at the various SecurityPermission annotation
declarations to see how I specified the value parameter's value.
As you peruse through the AnnotationsTest class above, you may wonder what the call
SecurityBlanket.checkPermission() is doing. This is where all the "magic" occurs.
Simply declaring a custom annotation and then decorating your code with it is not going
to accomplish much. The Java runtime has no idea what to do with your custom
annotations. They just occupy valuable memory space doing nothing. That's where the
SecurityBlanket.checkPermission() method call fits in.
First, this function figures out what the currently executing method is. Next, the function
determines if the caller's role is one of the permitted roles for the method. If the method is
a class constructor, then the function uses the roles specified in the SecurityPermission
annotation applied to the class declaration; otherwise it uses the roles specified in the
SecurityPermission annotation applied to the method declaration. The caller's roles are
specified on a per-thread basis by calling the SecurityBlanket.addPermission
method. In a real application, a custom JAAS (Java Authentication and Authorization
Specification) module that allows a user to log into your application may call the
SecurityBlanket.addPermission method on your behalf.
The point to realize from all this is that J2SE 5 provides us with a way of declaring our
own custom annotations and a way of decorating our own code with them. It also loads
them in the JVM and makes them available dynamically and reflectively. But then we
have to write the glue logic that actually makes something useful happen.

Page 21 of 27
The entire SecurityBlanket class is shown below:
public class SecurityBlanket
{
private static Logger logger = Logger.getLogger("AnnotationsTest");
private static HashMap<Thread, String> permissions = new
HashMap<Thread, String>();
public static void addPermission(String s)
{
permissions.put(Thread.currentThread(),s);
}
public static void removePermission()
{
permissions.remove(Thread.currentThread());
}
public static void checkPermission()
{
StackTraceElement e[]=Thread.currentThread().getStackTrace();
int i = 0;
for (i = 0; i <e.length; i++)
{
if (e[i].getClassName().equals("SecurityBlanket")
&& e[i].getMethodName().equals("checkPermission"))
break;
}
if (i == e.length)
{
throw new RuntimeException("Unexpected Security Error.");
}
logger.info("Checking security access to "
+ e[i+1].getClassName()
+ "::"
+ e[i+1].getMethodName());
try
{
Class c = Class.forName(e[i+1].getClassName());
if (e[i+1].getMethodName().equals("<init>"))
{
SecurityPermission permission =
(SecurityPermission)
c.getAnnotation(SecurityPermission.class);
if (permission != null)
{
String currentRole =
permissions.get(Thread.currentThread());
for (String role:permission.value())
{
if (role.equals("All"))
return;
else if (role.equals("None"))
{
throw new RuntimeException(
"Unauthorized access to class "
+ e[i+1].getClassName());
}
if (role.equals(currentRole))
return;

Page 22 of 27
}
}
return;
}
Method[] methods = c.getMethods();
for (Method m:methods)
{
if (m.getName().equals(e[i+1].getMethodName()))
{
SecurityPermission permission =
m.getAnnotation(SecurityPermission.class);
if (permission != null)
{
String currentRole =
permissions.get(Thread.currentThread());
for (String role:permission.value())
{
if (role.equals("All"))
return;
else if (role.equals("None"))
{
throw new RuntimeException(
"Unauthorized access to "
+ e[i+1].getClassName()
+ "::"
+ e[i+1].getMethodName());
}
if (role.equals(currentRole))
return;
}
}
break;
}
}
throw new RuntimeException("Unauthorized access to "
+ e[i+1].getClassName()
+ "::"
+ e[i+1].getMethodName());
}
catch (ClassNotFoundException ex)
{
logger.severe("Got an error: " + ex.getMessage());
throw new RuntimeException("Unexpected Security Error.");
}
}
}

The bulk of the code is straightforward. However, note the use of the getAnnotation()
method on the java.lang.reflect.Method and the java.lang.Class classes. These
are just two of the many new methods added to support reflectively retrieving annotations
during runtime.

Page 23 of 27
Finally, to see everything in action, let's add a main() method to the AnnotationsTest
class. The main() method creates two threads. Each thread simulates a different user with
a different role and calls all four public instance methods in the AnnotationsTest class.
The main method is shown below:
public static void main(String[] args)
{
try
{
Thread t1 = new Thread(new Test1());
t1.start();
t1.join();
Thread t2 = new Thread(new Test2());
t2.start();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}

And here are the Test1 and Test2 classes used by the main method:
class Test1 implements Runnable
{
public void run()
{
// Add the "HR" role for this user
SecurityBlanket.addPermission("HR");
try
{
AnnotationsTest test = new AnnotationsTest();
try
{
test.unRestrictedMethod("Hi");
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
try
{
test.fullyRestrictedMethod("Hi");
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
try
{
test.partiallyRestrictedMethod("Hi");
}
catch (Exception ex)
{

Page 24 of 27
System.out.println(ex.getMessage());
}
try
{
test.partiallyRestrictedMethod2("Hi");
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
finally
{
SecurityBlanket.removePermission();
}
}
}
class Test2 implements Runnable
{
public void run()
{
// Add the "Manager" role for this user
SecurityBlanket.addPermission("Manager");
try
{
AnnotationsTest test = new AnnotationsTest();
try
{
test.unRestrictedMethod("Hi");
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
try
{
test.fullyRestrictedMethod("Hi");
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
try
{
test.partiallyRestrictedMethod("Hi");
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
try
{
test.partiallyRestrictedMethod2("Hi");
}
catch (Exception ex)
{
System.out.println(ex.getMessage());

Page 25 of 27
}
}
finally
{
SecurityBlanket.removePermission();
}
}
}

When you run the AnnotationsTest class, you will see the following output:
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to AnnotationsTest::<init>
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to AnnotationsTest::unRestrictedMethod
Message from unRestrictedMethod: Hi
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to AnnotationsTest::fullyRestrictedMethod
Unauthorized access to AnnotationsTest::fullyRestrictedMethod
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to
AnnotationsTest::partiallyRestrictedMethod
Unauthorized access to AnnotationsTest::partiallyRestrictedMethod
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to
AnnotationsTest::partiallyRestrictedMethod2
Message from partiallyRestrictedMethod2: Hi
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to AnnotationsTest::<init>
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to AnnotationsTest::unRestrictedMethod
Message from unRestrictedMethod: Hi
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to AnnotationsTest::fullyRestrictedMethod
Unauthorized access to AnnotationsTest::fullyRestrictedMethod
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to
AnnotationsTest::partiallyRestrictedMethod
Message from partiallyRestrictedMethod: Hi
Mar 20, 2004 4:46:55 PM SecurityBlanket checkPermission
INFO: Checking security access to
AnnotationsTest::partiallyRestrictedMethod2
Message from partiallyRestrictedMethod2: Hi

Observe the following in the output:


The first thread (role set to "HR") has access only to the unRestrictedMethod() and
partiallyRestrictedMethod2() methods.
The second thread (role set to "Manager") has access to the unRestrictedMethod(),
partiallyRestrictedMethod(), and partiallyRestrictedMethod2()
methods.

Page 26 of 27
Neither thread has access to the fullyRestrictedMethod() method.
Both threads can access the unRestrictedMethod() method.
Both threads were able to create an instance of the AnnotationsTest class because
the class was annotated with the SecurityPermission annotation
SecurityPermission("All"). If All was changed to None, then neither thread
could have created an instance of the class.

The best way to see what's going on is to play around with the code and change the
values of the various SecurityPermission annotations that are sprinkled around the
AnnotationsTest class.
Finally, an awesome exercise would be to use AspectJ to define and implement the
appropriate pointcuts that would allow you to completely remove the manual call to
SecurityBlanket.checkPermission() from your code. Now that would be real
"magic," and AspectJ makes it easy.

Conclusion
This concludes my three-part series on J2SE 5. Along the way, I have covered many
interesting new language features, some more useful than others. I talked at length about
the new Java metadata facility called annotations in this article. Annotations provide us
with a host of new capabilities. Expect to see many tools in the next couple of years that
capitalize on Java annotations to provide new value-added services either at compile time
by code modification or at runtime.

Page 27 of 27

You might also like