devxlogo

J2SE 1.5: Java’s Evolution Continues

J2SE 1.5: Java’s Evolution Continues

he Java 2 Platform, Standard Edition (J2SE) 1.5 (codename: Tiger) has some language-level enhancements that Sun designed to make programmers’ lives much easier. This article takes a gander at some of these changes, presenting a number of small Java programs that will showcase some of the key Tiger value adds. (Click here to download the applications.)

How Do I Get a Peek?
At the time of this article’s writing, J2SE 1.5 was still in beta (February 2004 Beta 1 Release). You can download the SDK beta at java.sun.com/j2se. Installation of the beta is a straightforward point-and-click procedure.

One interesting feature of the beta is the ability to choose whether or not to use the new 1.5 features. When compiling your code, you have to explicitly state that you want the Java compiler to use the new 1.5 features. You do this with the following syntax:

javac source 1.5 YourClassName.java

I found the ability to specify this flag especially helpful in understanding how the compiler reacts with and without the new 1.5 feature support.

Parameterized Data Types (a.k.a. Generics)
One of Tiger’s key offerings is parameterized data types (also known as generics). generics deliver the ability to have type safe collections. The concept of generics might best be described with an example. In the past, Java programmers could put different object types into a List. Now, they can restrict the types of objects that can be placed in their List collections. If someone tries to place an object that is not of the originally specified datatype, Tiger throws a compile time exception. The following code demonstrates this:

import java.util.*;public class GenericExample{	public static void main (String args[])	{		// create an ArrayList and restrict contents 		// using  following your collection		ArrayList stringList =  new ArrayList();		// add a couple of Strings to the ArrayList		stringList.add("Apple");		stringList.add("Banana");		System.out.println(stringList);		// compiler will not allow the following since the 		// collection is type-safe (restricted to holding Strings)		// stringList.add(new Integer(8));		// no need to caste when extracting from collection		String fruit = stringList.get(1);		System.out.println("I can't hear you. I have a " 			+ fruit + " in my ear!");	}}

When you pull objects from your collection, you no longer have to explicitly caste them. In the code above, for example, you have String fruit = stringList.get(1); instead of String fruit = (String)stringList.get(1);.

As you read through this article, you’ll notice that Tiger essentially shifts some responsibility for code “writing” to the compiler. For example, in the example above, the compiler handles the explicit casting, which programmers had to perform in previous Java versions.

While playing around with the new Tiger compiler, I also noticed that it teaches the old J2SE dog new tricks. For example, if you try to create an ArrayList without using generics, the compiler warns you that your addition to the collection is “unchecked.” (You’ll see this firsthand later when you compile some code.)

Autoboxing and Auto-unboxing
Tiger converts primitive types to wrapper types and vice versa. For example, take a look at the following code found in AutoBoxExample.java:

public class AutoBoxExample{	public static void main (String args[])	{		handlePrimitive(new Integer(20));		handleWrapper(30);	}		// note: method would normally only handle primitives	private static void handlePrimitive(int prim)	{		System.out.println(prim);	}	// note: method would normally only handle wrappers	private static void handleWrapper(Integer wrapper)	{		System.out.println(wrapper);	}}

Note that you can pass an integer (primitive) where an Integer (wrapper object) is expected and vice versa. The Tiger compiler, which performs the necessary conversion for you, makes this possible. It assures that the proper type gets passed into your method, converting from primate to wrapper object or wrapper object to primitive as needed.

Enhanced For Loops
Another major language change in Tiger is enhanced for loops. The newly designed for loop is intended to allow easier integration through collections. In previous versions of Java, you had to use an iterator, as shown in the iterateOldWay method of the following class:

import java.util.*;public class EnhancedForLoopExample{	public static void main (String args[])	{		ArrayList arrayList  = new ArrayList();		arrayList.add("Apple");		arrayList.add("Banana");		iterateOldWay(arrayList);		iterateNewWay(arrayList);	}	// use of iterator required in previous versions of Java	public static void iterateOldWay(Collection coll)	{		for(Iterator i = coll.iterator(); i.hasNext();)		{			String grab = (String)i.next();			System.out.println(grab);		}	}		// new way to iterate through collection using enhanced for loop 	public static void iterateNewWay(Collection coll)	{		for (Object obj : coll)		{			System.out.println((String)obj);		}	}}

Take a look at the iterateNewWay method. Note the new for syntax. You are no longer restricted to providing pre and post conditions in your for statement. Your new syntax loops through each element of your collection.

To save time, the generics concept and enhanced for loops can be used in tandem. Notice how the following class leverages both concepts:

import java.util.*;public class EnhancedForLoopExampleWithGenerics{	public static void main (String args[])	{		// create an ArrayList and restrict contentsArrayList stringList =  new ArrayList();		// add a couple of Strings to the ArrayList		stringList.add("Apple");		stringList.add("Banana");				for (Object obj : stringList)		{			System.out.println(obj);		}		}}

Typesafe Enums
Tiger brings to the table a new type named enum. Using this keyword, one can define a set of valid values or states. Consequently, you can ensure type safety during compile time. For example, if you wanted to realize the concept of enumerations in the past, you might have used constants like in the following code:

public class TrafficLight{	public static final int GREEN = 1;	public static final int RED = 0;	public static final int YELLOW = 2;		// light is green by default	private int state = GREEN;		// get the current light state	public int getState()	{		return this.state;	}		// set the current light state 	// notice no protection of what the light can be set to	public void setState(int state)	{		this.state = state;	}}

The code of the EnumExample class demonstrates how you previously had to use a helper class to represent a set of states. Notice the absence of state setting safeguards. To elaborate, your states are represented by integers, and they have no safeguard to prevent the constructor of your TrafficLight from taking an integer greater than 2 (representing yellow). You could throw in some custom Boolean logic to add restrictions, but it would be quite cumbersome to create. Also, the numbers 0, 1, and 2 can lead to errors. You could accidentally assume that 0 represents green somewhere in your code. Now that would cause some traffic nightmares, wouldn’t it?

Take a look at the method newMethodologyWithEnum(). When you use the enum, you create a new enumeration and clearly define what your possible states can be. Switching between states is easier and your code does not rely on an auxiliary class anymore:

import java.util.*;public class EnumExample{	public static void main (String args[])	{		oldMethodology();			newMethodologyWithEnum();	}	public static void oldMethodology()	{		TrafficLight myLight = new TrafficLight();				// note that you could accidentally set your traffic 		//   light state to 4, which has no meaning				myLight.setState(TrafficLight.RED);				int stateOfLight = myLight.getState();		switch(stateOfLight)		{			case TrafficLight.GREEN:				System.out.println("Light is Green");				break;			case TrafficLight.RED:				System.out.println("Light is Red");				break;			case TrafficLight.YELLOW:				System.out.println("Light is Green");				break;		}	}	public static void newMethodologyWithEnum()	{		enum ImprovedTrafficLight {green,yellow,red};ImprovedTrafficLight improvedLight = ImprovedTrafficLight.green;		// switching states is easier		improvedLight = ImprovedTrafficLight.red;		// notice the easier syntax and no need for a helper object		switch(improvedLight) 		{			case green:				System.out.println("Light is Green");				break;			case red:				System.out.println("Light is Red");				break;			case yellow:				System.out.println("Light is Yellow");				break;								}	}}

Static Imports
Tiger’s static import feature eliminates the need to prefix static members of a class with class names. It’s similar to the package import facility you are used to (except that you import static members from a class rather than classes from a package). The following example class juxtaposes how you had to perform things in the past and how Tiger’s offerings can make your code much more abbreviated:

// static import syntaximport static java.util.Calendar.*;public class StaticImportExample{	public static String getCurrentMonthWithoutStaticImport()	{		int i = java.util.Calendar.getInstance().get(			java.util.Calendar.MONTH);		switch (i)		{		  case java.util.Calendar.JANUARY:		    return "January";		  case java.util.Calendar.FEBRUARY:		    return "February";		  case java.util.Calendar.MARCH:		    return "March";		  case java.util.Calendar.APRIL:		    return "April";		  case java.util.Calendar.MAY:		    return "May";		  case java.util.Calendar.JUNE:		    return "June";		  case java.util.Calendar.JULY:		    return "July";		  case java.util.Calendar.AUGUST:		    return "August";		  case java.util.Calendar.SEPTEMBER:		    return "September";		  case java.util.Calendar.OCTOBER:		    return "October";		  case java.util.Calendar.NOVEMBER:		    return "November";		  case java.util.Calendar.DECEMBER:		    return "December";		}		return null;	}		// example use of static imports 	// note the streamlined abbreviated syntax	public static String getCurrentMonthWithStaticImport()	{		int i = java.util.Calendar.getInstance().get(MONTH);		switch (i)		{		  case JANUARY:		    return "January";		  case FEBRUARY:		    return "February";		  case MARCH:		    return "March";		  case APRIL:		    return "April";		  case MAY:		    return "May";		  case JUNE:		    return "June";		  case JULY:		    return "July";		  case AUGUST:		    return "August";		  case SEPTEMBER:		    return "September";		  case OCTOBER:		    return "October";		  case NOVEMBER:		    return "November";		  case DECEMBER:		    return "December";		}		return null;	}	public static void main (String args[])	{		System.out.println(getCurrentMonthWithoutStaticImport());			System.out.println(getCurrentMonthWithStaticImport());		}		}

Formatting of Output
Tiger introduces the java.util.Formatter package, which enables you to justify and align text. It also provides common formats for numeric, string, and data/time data, as well as local-specific output. Inspired by the C programming language’s printf offering, this feature shares a lot of similarities with C. The class below shows a couple of examples of leveraging the Formatter package and its help in formatting. Also, note the use of a convenience method (i.e., System.out.format) to format the current time:

import java.lang.StringBuilder;import java.util.Formatter;import java.util.Locale;import java.util.Calendar;public class FormatterExample{	public static void main (String args[])	{		// Send all output to the Appendable object stringBuilder		StringBuilder stringBuilder = new StringBuilder();		// Construct a new formatter   		Formatter formatter = new Formatter(stringBuilder, Locale.US);			float balance = 500434.03F;		formatter.format("The current balance is: $ %(,.2f",                    balance);		System.out.println(stringBuilder);		// Prints out: The current balance is: $ 500,434.03		// Writes the current time to system out using the format 		// HH:MM:SS in 12 hour formatSystem.out.format("Local time: %tr", Calendar.getInstance());   		// sample output - 05:16:24		System.out.println();				// Writes the current time to system out using the format 		// HH:MM:SS in 24 hour (aka military) formatSystem.out.format("Local time: %tT", Calendar.getInstance());		// sample output - 17:16:24	}}

Variable Argument Lists
Tiger also introduces support for variable argument lists. In short, you can pass in a variable number of arguments by specifying (…). The simple class below shows how you can use a variable argument list in tandem with the enhanced for loops introduced previously. Note that the demonstrate method does not need to extract the length of your argument list, as it did in previous versions of Java.

public class VariableArgumentLists {         public static void demonstrate(String groupName, String... args)         {                         System.out.println("Group Name: " + groupName);                 for (String current: args)                 {                                 System.out.println(current);                 }                 System.out.println("-----------------");         }         public static void main(String args[])         {                 demonstrate("Stooges", "Larry","Moe","Curly");                 demonstrate("Marx Brothers", "Chico", "Harpo", "Groucho", "Gummo", "Zeppo");                 demonstrate("Laurel and Hardy", "Stan", "Ollie");         } }

Java’s Evolution Continues
Tiger epitomizes Java’s ongoing evolution and adaptation to the emerging needs of programmers and their applications. This article presented a sampling of what Tiger will offer. As you have seen, the common theme of the features is that the responsibility of “writing” code has shifted to the compiler behind the scenes. The release is being advertised as a “developer focused” release, and?as usual with Java?the new offerings don’t sacrifice backward compatibility. Features such as generics, autoboxing, and enhanced for loops go a long way toward making Java code easier to develop and read.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist