J2SE 5.0 is for Ease of Development
Tiger, Tiger burning bright
Like a geek who works all night
What new-fangled bit or byte
Could ease the hacker's weary plight?
One of the major themes targeted by the J2SE 5.0 release is ease of development. J2SE 5.0 provides new enhancements to further ease the development of Java applications and tools. New features such as generics, typesafe enumerated types, autoboxing, and enhanced for loop, are all new features that make using Java to build applications easier.
Tiger Feature List
The Tiger feature list is defined in JSR-176 (J2SE 5.0 (Tiger) Release Content), which serves as an umbrella JSR that only lists APIs defined in other component JSRs. Please see JSR-176 for details about the component JSRs.
The J2SE 5.0 release includes several new language features. The rest of this article discusses these language features and shows how to use them to improve programs.
Generics
To the most despised collections' cast
We'll bid a fond farewell at last
With generics' burning spear
The need for cast will disappear
The Collection
interface, which is implemented by all collection classes, suggests that all concrete collections are collections of Object
s at runtime. This places a burden on the developer of the calling class by forcing him to know the actual type of the elements in the collections, that can be accomplished by looking at the API or using the reflection API. As an example, consider the following class, Ex1
, which creates a collection of two strings and one integer, and then prints out the collection:
Ex1.java
import java.util.*; |
The problem here is that an explicit cast is required in the printCollection
method. This class compiles fine, but throws a CLassCastException
at runtime as it attempts to cast an Integer
to a String
:
Item: Hello world! |
Generics, which are also known as parameterized types, provide compile-time typesafety for collections and eliminate the drudgery of casting. Using generics, the Ex1
class above can be written as follows:
Ex2.java
import java.util.*; |
Now, if you try to compile this code, a compile-time error will be produced, informing you that you cannot add an Integer
to a collection of String
s. Therefore, generics enable more compile-time type checking and therefore mismatch errors are caught at compile time rather than at runtime.
You may have already noticed the new syntax used to create an instance of ArrayList
(i.e. List<String> list = new ArrayList<String>
). ArrayList
is now a generic or parameterized type. A parameterized type consists of a class or interface E
and a parameter section <T1, T2, ..., Tn>
, which must match the number of declared parameters of E
, and each actual parameter must be a subtype of the formal parameter's bound types. The following segment of code shows parts of the new class definition for ArrayList
:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable { |
Here E
is a type variable, which is an unqualified identifier. It simply acts as a placeholder for a type to be defined when the list is used.
Java Generics vs. C++ Templates
While generics look like the C++ templates, it is important to note that they are not the same. Generics simply provide compile-time type safety and eliminate the need for casts. The main difference is encapsulation: errors are flagged where they occur and not later at some use site, and source code is not exposed to clients. Generics use a technique known as type erasure
as described above, and the compiler keeps track of the generics internally, and all instances use the same class file at compile/run time.
A C++ template on the other hand is just a fancy macro processor; whenever a template class is instantiated with a new class, the entire code for the class is reproduced and recompiled for the new class.
Enhanced for Loop
While Iterators have their uses
They sometimes strangle us like nooses
With enhanced-for's deadly ray
Iterator's kept at bay
The current for
statement is quite powerful and can be used to iterate over arrays or collections. However, it is not optimized for collection iteration, simply because the iterator serves no other purpose than getting elements out of the collection. The new enhanced for
construct lets you iterate over collections and arrays without using iterators or index variables.
The new form of the for
statement has the following syntax:
for (FormalParameter : Expression) Statement |
Note that Expression
must be an array or an instance of a new interface called java.lang.Iterable
, which is meant to ease the task of enabling a type for use with the enhanced for
statement. This means that the java.util.Collection
now extends the Iterable
interface that has the following signature:
package java.lang; |
As an example, consider the following method that uses the conventional for
statement to iterate over a collection:
// Assume we have an instance of StringBuffer "sb" |
With the addition of generics, the above segment of code can be rewritten using the enhanced for
statement as follows:
// Assume we have an instance of StringBuffer "sb" |
Much cleaner, eh?
The enhanced for
statement can be used to iterate over an array. Consider the following segment of code for a method that calculates the sum of the elements in an array:
public int sumArray(int array[]) { |
Using the enhanced for
statement, this can be rewritten as:
public int sumArray(int array[]) { |
The new for
statement, however, is not a replacement for counting for
loops for arrays. Sometimes, the index variable serves more than just an array index -- when a different traversal order is needed, for example. In addition, the enhanced for
construct is a compiler 'trick' and can actually end up being slower when used for large collections.
Many developers are asking why they didn't add the foreach
and in
so that we can write:
foreach (element in collection) |
The fact of the matter is that adding new keywords to a language such as Java is quite costly. In addition, it wouldn't be compatible with existing source code that uses the in
keyword, for example, as in System.in
. So the whole idea is just to keep things upwardly-compatible.
Autoboxing/Unboxing
When from the collections ints are drawn
Wrapper classes make us mourn
When Tiger comes, we'll shed no tears
We'll autobox them in the ears
Manual conversion between primitive types (such as an int
) and wrapper classes (such as Integer
) is necessary when adding a primitive data type to a collection. As an example, consider an int
being stored and then retrieved from an ArrayList
:
list.add(0, new Integer(59)); |
The new autoboxing/unboxing feature eliminates this manual conversion. The above segment of code can be written as:
list.add(0, 59); |
However, note that the wrapper class, Integer
for example, must be used as a generic type:
List<Integer> list = new ArrayList<Integer>(); |
Programming language critics may have a lot to say about the autoboxing/unboxing feature. On one hand, Java developers agree that the distinction between primitive data types and references can be burdensome. In a pure object-oriented language there should be no difference between a primitive data type and a reference, as everything is an object. The other issue is that of identity: many think of primitive data types as entities that represent mathematical values, which are different from references.
The purpose of the autoboxing/unboxing feature, however, is simply to ease the interoperability between primitive types and references without any radical changes.
Typesafe Enumerations
The int-enum will soon be gone
Like a foe we've known too long.
With type safe-enum's mighty power
Our foe will bother us no more
An enumerated type is a type whose values consist of a fixed set of constants. The C-style enumerated type, enum
was omitted from the Java programming language. A commonly used pattern for enumerated types in Java is to define a class of constants as follows:
public class MainMenu { |
This pattern has several drawbacks including:
- It is not type safe
- Constants are compiled into clients and therefore adding more constants require recompilation of the clients
- It has no namespace and therefore must prefix constants
- Provides no easy way to translate the constants into informative printable values
In addition to the above, and as we all know, an object is an instance of a class. An object has state (represented by variables) and behavior (represented by methods). The above MainMenu
class has state only. So from an object-oriented point of view, it is not really a class since its instances or objects would have no behavior.
A type safe enum pattern that avoids all of the above problems has been proposed by Joshua Bloch in his book "Effective Java Programming Language Guide". The idea is to define an enum as a class that represents a single element of the enumerated type; such a class must not provide public constructors. Here is an example:
public class MainMenu { |
Using MainMenu
, there is no way for a client to create an object of the class or extend it. This is a compile-time typesafe enumerated type: it is guaranteed that if you declare a method with a parameter of type MainMenu
, then any non-null object reference passed in represents one of the four valid menu items. Any attempt to pass an incorrectly-typed object results in a compile-time error. In addition, this is a truly object-oriented class as it contains both state and behavior.
In J2SE 5.0, however, you do not need to worry about inventing your own enumeration patterns since it provides a typesafe enumerated type facility. The J2SE 5.0 enum
declaration looks as follows:
public enum MainMenu {FILE, EDIT, FORMAT, VIEW}; |
This syntax looks much like enums in other languages such as C/C++, but in C/C++ enums are simply glorified integers where in Java a full-fledged class is generated for each enum type. This approach has many advantages including:
- It provides strong compile-time type safety
- It provides a separate namespace for each enum type and thus eliminates the need to include a prefix in each constant name
- Constants are not compiled into clients so you can freely add, remove, or reorder them without recompiling the clients
- Printed values are informative instead of just numbers
- Enum constants can be used wherever objects can be used
There are a couple of important things to note about enum declarations. As an example, consider the following declaration:
public enum MainMenu {FILE, EDIT, FORMAT, VIEW}; |
- The word
enum
is reserved and therefore if you have been using it as an identifier, you should adjust your code when compiling with a J2SE 5.0 compiler. - The above
enum
declaration generates a class (MainMenu
in the above example), which automatically implements theComparable<MainMenu>
andSerializable
interfaces, and provides several members including:- Static variables
FILE
,EDIT
,FORMAT
, andVIEW
- Static method
values()
, which is an array containing the constants in the enum - static method
valueOf(String)
that returns the appropriate enum for the string passed in - Appropriately overloaded
equals()
,hasCode
,toString()
, andcompareTo()
methods.
- Static variables
Here is a complete example that declares an enumeration and then prints the values:
public class Example { |
And the following segment of code shows another example using the switch
statement:
for(MainMenu menu : MainMenu.values()) { |
It is worth noting that two classes have been added to java.util
in support of enums: EnumSet
(a high-performance Set
implementation for enums; all members of an enum set must be of the same enum type) and EnumMap
(a high-performance Map
implementation for use with enum keys).
Static Imports
And from the constant interface
We shall inherit no disgrace
With static import at our side
Our joy will be unqualified
The static import feature enables you to import static members from a class or an interface and thus use them without a qualifying name. As an example, consider the following interface that contains two constant values:
package com.name; |
Now, the constants in the XYZ
interface can be used as follows:
public class MyClass implements XYZ { |
As you can see, a class must implement the interface in order to have access to the constants defined in the interface.
In J2SE 5.0, static import solves this problem as shown in the following example:
import static com.name.XYZ.*; |
As another example, consider the following static imports:
import static java.lang.Math.*; |
With these two static imports, you now can use sin(x)
instead of Math.sin(x)
, and out.println("Hello there");
instead of System.out.println("Hello there");
.
Metadata
As for noble metadata
I'll have to sing its praises later
Its uses are so numerous
To give their due, I'd miss the bus
The J2SE 5.0 metadata feature is a facility that allows developers to annotate their code so that tools can generate boilerplate code (e.g stub generation to remote procedure calls) as directed by annotations. This facility allows for parsing of your Java files and generating artifacts such as XML descriptors or source code. For more information on the metadata facility, please see A Metadata Facility for the Java Programming Language.
Others
In addition to the new language constructs and features, several other enhancements have been introduced. This section provides an overview of some of the significant enhancements.
Variable Arguments:
O joyless nights, o joyless days
Our programs cluttered with arrays
With varargs here, we needn't whine;
We'll simply put the args inline
The variable arguments new functionality in J2SE 5.0 allows multiple arguments to be passed as parameters to methods as in:
void someMethod(Object ... args) { |
The notation ...
is required. The printf
statement, which is discussed later, is an example of using variable arguments.
Formatted Output: The variable arguments functionality has been used to implement the flexible number of arguments required for printf
. That is right! J2SE 5.0 provides C-like printf
functionality, so now it is possible to easily format output using printf
:
System.out.printf("%s %3d", name, age); |
Enhanced Input: Prior to J2SE 5.0, in order to read an integer value from the keyboard, it has to be read as a String
and then parsed as follows (this code doesn't include the try and catch constructs):
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); |
In J2SE 5.0, the java.util.Scanner
class can be used to accomplish the same thing but with less code as follows:
Scanner reader = new Scanner(System.in); |
Programming educators are going to love this enhanced input functionality, as it will make it easy to explain how to read primitive data types from the keyboard.
If you need to process more complex input, use the java.util.Formatter
class, which includes pattern matching algorithms.
Synchronization: J2SE 5.0 provides higher-level synchronization constructs in the form of a comprehensive library of concurrency utilities (java.util.concurrent
) containing thread pools, queues, concurrent collection, special purpose locks, and barriers. This is a substantial addition that will change the way we develop concurrent Java applications. Stay tuned for an article that explains this new functionality.
Conclusion
Tiger, Tiger burning bright
Like a geek who works all night
What new-fangled bit or byte
Could ease the hacker's weary plight?
The Tiger release has introduced several new language constructs, features, and enhancements in order to further ease the development of Java applications. The code examples included in this article demonstrate how easy it is to use the new language features to improve the readability of your programs. While some of the new features, such as generics and enumerated types, do exist in other programming languages, Java has made them compile-time typesafe in order to allow you to develop safer applications with Java than with other programming languages!
Have fun with the Tiger and stay tuned for more articles that demonstrate how to use some of the more advanced features such as synchronization and metadata.
For More Information
Download J2SE 5.0
J2SE 5.0 Name and Version Change
J2SE 5.0 (Tiger) Release Contents
New Features and Enhancements in J2SE 5.0
J2SE 5.0 in a Nutshell
A Conversation with Joshua Bloch
JavaOne 2003 Technical Session 3072 (Forthcoming Java Programming Language Features)
No comments:
Post a Comment