This document was uploaded by user and they confirmed that they have the permission to share
it. If you are author or own the copyright of this book, please report to us by using this DMCA
report form. Report DMCA
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 type boolean byte double short int long float
Reference type Boolean Byte Double Short Integer Long 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)
} // 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 zerobased 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;
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.