November 22, 2014
Hot Topics:

Generics in J2SE 5.0

  • April 5, 2005
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming, Notes # 2300


Preface

Many new features in J2SETM 5.0

J2SETM 5.0 contains many new language features, including:

  • Generics
  • Enhanced for Loop
  • Autoboxing/Unboxing
  • Typesafe Enums
  • Varargs
  • Static Import
  • Metadata

In addition to the new language features, J2SETM 5.0 contains many other new features such as new Look and Feel capabilities for Swing GUIs.

First in a series

This is the first lesson in a series designed to teach you how to use some of the new features in J2SETM 5.0.  This lesson will teach you some of the rudimentary aspects of the new capability commonly referred to as generics.  This lesson will also teach you how to use the enhanced for loop with collections and also with arrays.

Future lessons will teach you how to use other aspects of generics and in addition will teach you how to use some of the other new features in J2SETM 5.0.

What is J2SETM 5.0?

Many of my students have become confused by references to Java version 1.5, J2SETM 5.0 and JDK 5.0.  In case this is also confusing to you, I will attempt to guide you through this nomenclature.  If you already understand this nomenclature, just skip ahead to the section entitled Viewing tip.

As of the date of this writing, you can freely download JDKTM 5.0 from Sun's web site at the following URL:

http://java.sun.com/j2se/1.5.0/download.jsp

A little historical perspective

Back in 1998 or 1999 when Java version 1.2 was released, Sun decided to refer to that version as Java 2.  According to Sun:

"... the "2" in Java 2 Platform Standard Edition indicates the 2nd generation Java platform"

Also, around that time, (or probably a little later), they separated Java into three editions:

  • Standard Edition - also known as J2SE
  • Enterprise Edition - also known as J2EE
  • Micro Edition - also known as J2ME

What is the difference between the different editions?

Among other things, the different editions have different class libraries where the different class libraries have a different emphasis.  I believe that at least the first two editions listed above have the same compiler and the same virtual machine.  I'm not sure about the Micro Edition because I have never worked with it.

Java 2 has remained at the forefront

In any event, as the released versions have progressed through v1.2, v1.3, and v1.4, Sun has continued to refer to the product as Java 2, as exemplified by J2SE, J2EE, and J2ME.

Now there is an additional number

With the release of version 1.5, Sun has now attached another number to the common name for the product.  If you go to the URL given above, you will find that the web page is entitled:

J2SE 5.0
Download Java 2 Platform Standard Edition 5.0

If things weren't confusing enough already, we how have Java 2 Platform Standard Edition 5.0.  Thus, 5.0 has been added to the Java 2 nomenclature.

According to sun (boldface added for emphasis)

"Both version numbers "1.5.0" and "5.0" are used to identify this release of the Java 2 Platform Standard Edition. Version "5.0" is the product version, while "1.5.0" is the developer version. The number "5.0" is used to better reflect the level of maturity, stability, scalability and security of the J2SE."

What is JDKTM 5.0?

According to Sun, the following two products are delivered under the JavaTM 2 Platform Standard Edition 5.0, also known as J2SETM 5.0.

(If you are going to develop Java programs, you need the JDK, which includes the JRE.  If you are only going to run Java programs, you only need the JRE.)

Downloading JDKTM 5.0

If you scroll down the download page a bit, you will find a section of the page that reads something like the text in Figure 1.  The two boldface lines in Figure 1 are download links on Sun's web page.

JDK 5.0 Update 1 includes the JVM technology

The J2SE Development Kit (JDK) supports creating J2SE applications. More info...

Download JDK for Windows
Download JDK for Other Platforms

Figure 1

What version is this?

If you download and install the JDK for Windows by selecting the link on Sun's web page and then enter java -showversion at the command prompt, your output should be similar to Figure 2 (boldface added for emphasis).  When I did this, I got java version 1.5.0_01.  I assume that this is also true for the other platforms as well, although the last three digits in the version number are likely to change with time as Sun releases new versions designed to fix bugs.

C:\jnk>java -showversion
java version "1.5.0_01"
Java(TM) 2 Runtime Environment, Standard Edition
 (build 1.5.0_01-b08)
Java HotSpot(TM) Client VM (build 1.5.0_01-b08,
 mixed mode, sharing)
		
Figure 2

The bottom line

Therefore, J2SETM 5.0 is the name that Sun has given to the Java 2 Platform Standard Edition developer version 1.5.0 or later.  (At this point, I don't know if this terminology is also being used with the Enterprise Edition and the Micro Edition.)

So now you know that when someone refers to J2SETM 5.0, they are also referring to Java version 1.5.0.

What happened to the SDK?

For the past several years, Sun has referred to the Software Development Kit (SDK) either as an alternative to or in addition to the JDK.  Here is what Sun has to say about that now (boldface added for emphasis):

"Due to significant popularity within the Java developer community, the development kit has reverted back to the name "JDK" from "Java 2 SDK" (or "J2SDK"), and the runtime environment has reverted back to "JRE" from "J2RE".  Notice that "JDK" stands for "J2SE Development Kit" (to distinguish it from the J2EE Development Kit). The name "Java Development Kit" is no longer used, and has not been officially used since 1.1, prior to the advent of J2EE and J2ME."

More than you ever wanted to know

And that is probably more than you ever wanted to know about such things as

  • JavaTM developer version 1.5.0
  • JavaTM product version 5.0
  • JavaTM 2 Platform Standard Edition 5.0, also known as J2SETM 5.0
  • J2SETM Development Kit 5.0, also known as JDKTM 5.0
  • Java 2 SDK, also known as J2SDK
  • J2SETM Runtime Environment 5.0, also known as JRE 5.0

What should you download?

However, when you need to download and use the JavaTM developer version 1.5.0, it helps to know where to find it. This is a problem because the nomenclature 1.5 doesn't appear anywhere on Sun's download page as of the date of this writing in March of 2005.  You must dig much deeper into Sun's web site to find the information that relates the product version 5.0 to the developer version 1.5.0 as described above.

(Or, you can do as I did, which is to take a chance, download JDK 5.0, install it, and test it to see if it is really version 1.5.0.x.)

Downloadable documentation doesn't match online documentation

The documentation that I downloaded at that time stated

"Its external version number is 5.0 and internal version number is 1.5.0."

The description of the product version and developer version in place of external version and internal version plus a link to an additional document entitled Version 5.0 or 1.5.0? appears to have been added to the online documentation at a later point in time.  In fact, as of the date of this writing in March of 2005, the downloadable JDKTM 5.0 documentation still hasn't been updated to match the online documentation in this regard.

So much for all of that.  Now on to other and possibly more interesting topics.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different figures and listings while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

Background Information

A new compiler warning

Have you recently tried to recompile a legacy program that has compiled and executed properly for many years only to get the following message from the compiler?

Note: ProgramName.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

If you followed up and recompiled as directed, you probably got a message from the compiler that looked something like the following:

ProgramName.java:37: warning: [unchecked] unchecked call to
add(E) as a member of the raw type java.util.ArrayList
var1.add(new Date());

If so, congratulations, you have had your first encounter with a new feature in J2SE 5.0 known as generics.

What are generics?

Sun has this to say about generics (boldface added for emphasis):

"This long-awaited enhancement to the type system allows a type or method to operate on objects of various types while providing compile-time type safety.  It adds compile-time type safety to the Collections Framework and eliminates the drudgery of casting."

In addition to the Collections Framework, generics impacts several other areas of Java programming as well.

Generics are not particularly easy

Don't be lulled into a false sense of security by what you will find in this lesson.  Although the introductory material presented in this lesson is rather straightforward, a full understanding of generics can be fairly difficult.  The tentacles of generics reach into many different areas of Java in very complex ways.

The truth of this is borne out by the fact that the excellent book entitled Java How to Program, Sixth Edition (Deitel) dedicates an entire chapter consisting of more than 30 pages to generics.  Furthermore, the chapter on generics doesn't even include many additional pages that the book dedicates to a discussion of the impact of generics on the Java Collections Framework.  The book covers several topics in the chapter on generics,  including the following:

  • Generic methods
  • Generic classes
  • Raw types
  • Wildcards
  • Generics and inheritance

The impact of generics on the Java Collections Framework is covered in a different chapter.  I hope that the benefits provided by generics outweigh the additional complexity.  Only time will tell.

The Java Collections Framework

Even though the material that will be presented in this lesson will be relatively straightforward, you will need to know something about the Java Collections Framework for it to make much sense.  If you need to learn more about the Java Collections Framework, see the lessons beginning with lesson 1350 and extending through lesson 1380 at http://www.dickbaldwin.com/tocint.htm.  Be aware, however, that those lessons were published long before generics existed in Java.  If you compile the programs in those lessons using J2SETM 5.0, you will get the warning messages from the compiler that I mentioned earlier.

Preview

I will present and explain seven simple programs in this lesson.  Those programs are intended to illustrate the following concepts:

  • How the Java Collections Framework behaved prior to the release of J2SE 5.0.
  • The effect of an incorrect cast in code that doesn't use generics.
  • Avoiding the requirement to cast through the use of generics.  This program also includes an illustration of some of the required syntax for generics.
  • Compile-time type safety provided by the use of generics.
  • Syntax requirements for the use of iterators with generics.  This program also illustrates the use of the enhanced for loop with collections, which is another new feature in J2SETM 5.0.
  • Use of the enhanced for loop with array objects.
  • How to achieve pre-generic behavior with a collection and also eliminate the compiler warnings.

Discussion and Sample Code

Listings of the programs

Complete listings of all the programs discussed in this lesson are provided in Listing 11 through Listing 17 near the end of the lesson.

Collection behavior prior to J2SETM 5.0

The main purpose of the program named Generics01 is to establish a baseline against which to compare the other programs.  A secondary purpose is to illustrate the warnings produced by the J2SETM 5.0 compiler when the syntax of the source code doesn't take generics into account.

A complete listing of the program named Generics01 is shown in Listing 11 near the end of the lesson.

As you will see later, this program was written using the program syntax and style that was correct prior to the release of J2SETM 5.0.  In particular, this program does not include the syntax necessary to take generics into account.

Notes at compile time

When this program is compiled using the J2SETM 5.0 compiler, the text shown in Figure 3 appears on the screen.

Note: Generics01.java uses unchecked or unsafe
operations.
Note: Recompile with -Xlint:unchecked for
details.
		
Figure 3

Note that the text in Figure 3 is not identified as either an error or a warning.  Rather, the text is identified simply as notes.  These notes provide instructions on how to recompile and get more information regarding a potential problem.

Recompiling with the Xlint switch

When the program is recompiled using the Xlint switch shown in Figure 3, the compiler produces the text shown in Figure 4.  Note that this text is identified as a warning.

(Later when we examine the code from the program named Generics01, you can compare it with this text to see just what the compiler is complaining about.)

Generics01.java:34: warning: [unchecked] 
unchecked call to add(E) as a member of
 the raw type java.util.ArrayList
    var1.add(new Date());
            ^
1 warning
		
Figure 4

Get used to it

The text in Figure 3 and Figure 4 is similar to what you can expect to see any time that you use the J2SETM 5.0 compiler to compile a program that includes the Java Collections Framework and doesn't use the required syntax to take generics into account.  This probably includes many of the programs that you wrote, compiled, and executed successfully prior to the release of J2SETM 5.0.

The program code

The code for the program named Generics01 is shown in its entirety in Listing 1 (and repeated in Listing 11 for your convenience in locating it later).

import java.util.*;

public class Generics01{
  
  ArrayList var1 = new ArrayList();

  void runIt(){
    var1.add(new Date());
    //Note the required cast in the following
    // statement.
    System.out.println(
                  ((Date)var1.get(0)).getTime());
  }//runIt

  public static void main(String[] args){
    new Generics01().runIt();
  }//end main
  
}//end class Generics01

Listing 1

The main method

As you can see, the main method in Listing 1 instantiates a new object of the class named Generics01, and invokes the method named runIt on that object.  It is the behavior of the runIt method that interests us.  More particularly, it is the expression highlighted in boldface in Listing 1 that interests us the most.

A baseline program

As mentioned earlier, the main purpose of this program is to establish a baseline against which we can compare the other programs to be discussed later.  This program was written exactly as it would have been written prior to the release of generics in J2SETM 5.0.

An ArrayList object

The program declares and initializes an instance variable named var1 with a reference to an object instantiated from the class named ArrayListArrayList is one of the concrete implementations of the interfaces provided in the Java Collections Framework.  Briefly, an object of the ArrayList class is an object that implements the List interface, providing a convenient place to store references to other objects.

One of the methods of an ArrayList class is the method named add.  This method is used to add new elements to the end of the list.

Adding a Date object's reference to the list

Listing 1 invokes the add method to add a new Date object's reference to the list.

(This is the method invocation that the compiler was complaining about in the warning in Figure 4.)

When an object's reference is added to an ArrayList object (without the use of generic syntax), that reference is automatically converted to and stored as type Object.

(That is probably also true even with the use of generic syntax.  I will have more to say about this later.)

What can you do with a reference of type Object?

I often tell my students that there are only twelve things that you can do with an object's reference that has been converted to type Object.  The first eleven of those twelve things is to invoke any one of the eleven methods that are defined in the Object class and inherited into all subclasses of Object.  The twelfth thing is to attempt to cast the reference to some other type in an attempt to invoke some other method on the reference after casting.

Invoking getTime on the reference

The objective of the code in this program is to invoke the method named getTime on the Date object's reference.  The getTime method is not one of the eleven methods that are defined in the Object class.  Rather, it is defined in the Date class.

Change the type of the reference

Therefore, in order to invoke the getTime method on the reference after it is retrieved from the ArrayList object, the type of the reference must be converted from type Object to type Date.  This is accomplished by the cast operation in the expression that is highlighted in boldface in Listing 1.

The boldface code in Listing 1 invokes the get method on the reference to the ArrayList object to retrieve the element stored at index 0 of the collection.  The get method returns the reference as type Object (at least that was true prior to the introduction of generics).  Then the boldface expression in Listing 1 casts the reference to type Date, converting its type back to type Date

Invoking the getTime method

Finally, the boldface expression successfully invokes the getTime method on the reference of type Date.

(Note that the program won't compile without the cast.)

The program produces an output similar to that shown below:

1110490974540

This is the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by the Date object.

Casting was a necessity prior to J2SETM 5.0

Prior to the release of J2SETM 5.0, it was always necessary to cast references retrieved from collection objects in order to invoke any methods on them other than the eleven methods defined in the Object class.

Some authors refer to this casting requirement as "the drudgery of casting," and indicate that casting may be eliminated through generics.

(In my opinion, from this viewpoint alone, the cure is worse than the disease.  Casting syntax is much simpler and more straightforward than generics syntax.)

May eliminate runtime errors and exceptions

However, it is possible for the programmer to perform an incorrect cast at this point in the program, which will usually result in a ClassCastException being thrown at runtime.

The great promise of generics is that it can sometimes cause programming errors to be recognized at compile time instead of encountering them at runtime.

Obviously, the best approach is to avoid writing programs containing programming errors in the first place.  However, if you are going to write programs containing errors, it is usually better to catch them at compile time than to have them occur at runtime.  This is the thing that may make the complexity of generics worthwhile.

An incorrect cast in code that doesn't use generics

The program named Generics02 (shown later in Listing 2) illustrates the application of an incorrect cast to an element that is retrieved from an ArrayList object, along with the runtime error that is produced by that incorrect cast.

No compiler error

This program does not produce a compiler error.

(However, the J2SETM 5.0 compiler does produce a warning having to do with the failure to apply the new generics syntax that was released in J2SETM 5.0.  Note that earlier compilers would not have produced such a warning.)

The point here is that the compiler does not check to confirm that the correct cast is applied.  This results in a successful compilation, but the program throws an exception at runtime.

A ClassCastException

The runtime exception that is thrown by this program is shown in Figure 5.  You can compare the details of this exception with the program code later.  The reference to line 46 in Figure 5 is a reference to the boldface statement in Listing 2.

Exception in thread "main" 
java.lang.ClassCastException: java.lang.String
        at Generics02.runIt(Generics02.java:46)
        at Generics02.main(Generics02.java:53)
		
Figure 5

The program code

The program is shown in its entirety in Listing 2 below, (and also in Listing 12 near the end of the lesson).  As before, the main method instantiates an object of the Generics02 class and invokes the runIt method on that object.  Also as in the previous program, this program instantiates a new ArrayList object and saves the object's reference in the instance variable named var1.

import java.util.*;

public class Generics02{
  
  ArrayList var1 = new ArrayList();
  
  void runIt(){
    var1.add(new Date());
    var1.add("abcd");
    System.out.println(
                  ((Date)var1.get(1)).getTime());
    System.out.println(
                 ((String)var1.get(1)).length());
  }//end runIt

  public static void main(String[] args){
    new Generics02().runIt();
  }//end main
  
}//end class Generics02

Listing 2

The runIt method

The runIt method begins by populating the ArrayList object with references to two different objects of different types.  One of the objects is type Date.  The other object is type String.

A questionable programming style

While this is probably not a very good programming style, it is a style that was commonly used by Java programmers prior to the advent of generics.  It was common to populate collection objects with references to a mixture of objects of different types. 

(The use of generics strongly discourages this programming style, although as you will see later, it is still possible even with generics.)

References are stored as type Object

As you already know, when these object's references are put into the collection, the types of all the references are automatically converted to type Object.  As you also already know, when the references are retrieved from the collection, if the purpose is to invoke any method on a reference other than one of the eleven methods defined in the Object class, it is necessary to cast the reference to a type that is consistent with the method.

Casting errors are likely

This is a scenario where the programmer is likely to make a casting error when casting elements retrieved from the collection.  Unless the programmer uses the instanceof operator to determine the type of a retrieved reference prior to performing the cast, the programmer is depending on his memory to know the type of each reference in the population on the basis of the index of the element.  If the programmer loses track of the types of the different references with respect to the element's indices, a casting error is a strong possibility.

The error scenario

This is the error scenario depicted by the boldface statement in Listing 2.  In this case, the element at index 0 is a reference to a Date object, and the element at index 1 is a reference to a String object.  However, the programmer mistakenly retrieves the element at index 1 and attempts to cast it to type Date, which is the type of the reference at index 0.  This results in the runtime exception shown in Figure 5.

Of course, it has always been possible to use the instanceof operator to confirm the type of a reference before performing a cast as a way to avoid this type of programming error.  Good programming practice would dictate the use of that construct when working with references to objects of mixed types in a single collection.

Avoiding the requirement to cast through the use of generics

The program named Generics03 shown in Listing 3 below, (and also in Listing 13 near the end of the lesson) illustrates how generics can be used to avoid the requirement to cast references when they are retrieved from a collection.

(While casting is not difficult, avoiding the requirement to cast can also avoid the possibility of casting incorrectly.)

As in the previous programs, the main method in this program instantiates an object of the class named Generics03 and invokes the method named runIt on the object.

import java.util.*;

public class Generics03{
  
  ArrayList <Date> var1 = new ArrayList<Date>();

  void runIt(){
    var1.add(new Date());
    //Note that no cast is required in the
    // following statement.
    System.out.println(var1.get(0).getTime());
  }//end runIt

  public static void main(String[] args){
    new Generics03().runIt();
  }//end main
  
}//end class Generics03

Listing 3

Instantiate an ArrayList object

Also, as in the previous programs, this program declares an instance variable named var1 and initializes that variable with a reference to a new object of type ArrayList.  However, the syntax that is used for this purpose in this program is significantly different from the syntax used for the same purpose in the previous two programs.

(Note the code in boldface that shows the type Date enclosed in matching angle brackets, as in <Date>.  This is the primary syntax change required to use generics.)

What does this syntax mean?

One way to think of this syntax is that the expression on the right of the assignment operator containing <Date> instantiates a new ArrayList object that is capable of containing only references to objects of type Date.

(Those references are still stored as type Object.  We will see how this apparent discrepancy is reconciled later through automatic casting.)

Similarly, the expression on the left of the assignment operator is the declaration of an instance variable capable of holding a reference to an ArrayList object, which in turn is capable of containing only references to objects of type Date.

Must qualify both expressions

It is necessary to qualify the expressions on both sides of the assignment operator by use of the syntax <Date>.  If the qualifier is included in the expression on the right, but is omitted from the variable declaration on the left, the compilation fails later when the code retrieves the reference.  The reference is retrieved as type Object instead of type Date, which is inconsistent with the method named getTime.

If the <Date> qualifier is included with the variable declaration on the left and omitted from the instantiation of the new object on the right, the program compiles and runs successfully.  However, the compiler issues an unchecked conversion warning indicating the possibility of a runtime error under certain conditions.

(I will have more to say about the importance of such warnings later.)

What does Sun have to say?

According to Sun (boldface added for emphasis),

"Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection."

Note the boldface text in the above quotation indicating that the compiler modifies your code by inserting casts where appropriate.

Compile-time type safety

In discussing code similar to the code in Listing 3, Sun states,

"... so the compiler can verify at compile time that the type constraints are not violated at run time. Because the program compiles without warnings, we can state with certainty that it will not throw a ClassCastException at run time. The net effect of using generics, especially in large programs, is improved readability and robustness."

In order to achieve compile-time type safety, it is necessary that the program compiles without warnings.  Otherwise, the program may execute, but may throw a ClassCastException at runtime.

More on generics with collections

In further explaining generics as used with collections, Sun goes on to say (boldface added for emphasis):

"... when we declare c to be of type Collection<String>,  this tells us something about the variable c that holds true wherever and whenever it is used, and the compiler guarantees it (assuming the program compiles without warnings). A cast, on the other hand, tells us something the programmer thinks is true at a single point in the code, and the VM checks whether the programmer is right only at run time."

The bottom line

The bottom line on generics (when used with collections) seems to be that references to objects are still stored in the collection as type Object.  However, when we notify the compiler of the type of data to be stored in the collection using syntax such as <Date>, and the program compiles without warnings, the compiler will do at least the following:

  • Ensure that only references to objects of the specified type are stored in the collection, and used consistently throughout the program, thus eliminating the possibility of a ClassCastException at runtime.
  • Automatically cast the reference to the specified type when it is later retrieved by program code.

No explicit cast is required

That brings us back to a discussion of the code in Listing 3.  Note that unlike the code in Listing 1, the print statement in Listing 3 does not contain an explicit cast to type Date, (at least not in the code that I wrote).

As described above, having been notified that the collection can contain only references to objects of type Date, the compiler automatically inserted a cast to type Date at the appropriate place in the code, thereby guaranteeing that the reference is converted from type Object to type Date before the getTime method is invoked on the reference. 

There is still a cast involved.  However, the cast is automatically inserted by the compiler.  This eliminates the requirement for me (the programmer) to insert the cast, and also eliminates the possibility of me inserting an incorrect cast.

Once again, all of this assumes that the program compiles without warnings.

Program output

The program in Listing 3 compiled and executed correctly, producing the following output for one particular run.

1110504030101

Compile-time type safety provided by the use of generics

The program named Generics04 shown in Listing 4 (and also in Listing 14) illustrates the ability of generics to prevent the storing of the wrong type of references in a collection.  This in turn can prevent runtime errors.

import java.util.*;

public class Generics04{
  
  ArrayList <Date> var1 = new ArrayList<Date>();
  
  void runIt(){
    var1.add("abcd");
    System.out.println(var1.get(0).getTime());
  }//end runIt

  public static void main(String[] args){
    new Generics04().runIt();
  }//end main

}//end class Generics04

Listing 4

A new ArrayList object

The boldface statement in Listing 4 instantiates a new object of type ArrayList capable of storing only references to objects of type Date.  This object's reference is stored in the instance variable named var1.

(This statement is identical to the statement used for the same purpose in the program in Listing 3.)

Other types cannot be stored in the collection

Once the ArrayList has been constrained to contain only references to objects of type Date, the compiler will not allow a reference to an object of any other type (other than types that are assignment compatible with Date, such as subclasses of Date) to be stored in the collection.

(I will discuss the impact of generics on the inheritance hierarchy in a future lesson.)

A compiler error rather than a runtime error

The first statement in the runIt method in Listing 4 attempts to add a new element to the ArrayList object.  The new element is a reference to a literal String object that encapsulates the string "abcd".  This results in the compiler error shown in Figure 6.

Generics04.java:34: cannot find symbol
symbol  : method add(java.lang.String)
location: 
  class java.util.ArrayList<java.util.Date>
    var1.add("abcd");
        ^
1 error
		
Figure 6

Without the use of generics, a reference to an object of any type could be added to the collection.  This could result in a runtime error later if the programmer expected an object of type Date when in fact the object is of type String.  If you are going to write programs containing errors, compiler errors are almost always preferable to runtime errors.

Generic iterator syntax and the enhanced for loop

Iterators

Listing 3 and Listing 4 showed you the syntax that you must use to cause a collection object to be treated as a generic collection.  You must also use a special syntax when working with generic iterators and the Java Collections Framework.

The enhanced for loop

Another new feature of J2SETM 5.0, referred to by Sun as an enhanced for loop can be used in certain situations to provide most of the benefits of an iterator with a somewhat simpler syntax. 

(The enhanced for loop is also sometimes referred to as a for-each loop.)

The program named Generics05, which begins in Listing 5, illustrates both of these concepts.

The main method

As in the previous programs, the main method instantiates an object of the Generics05 class and invokes the runIt method on that object.  You can view the main method as part of a listing of the entire program named Generics05 in Listing 15 near the end of the lesson.

Instantiate a generic ArrayList object

This program is a little longer than the previous programs, so I will break it down and explain it in fragments.

Listing 5 shows the beginning of the Generics05 class.  The code in Listing 5 instantiates a new ArrayList object capable of storing references to objects of type Date only.  The code in Listing 5 also saves that object's reference in a generic instance variable named var1.  This is the same syntax that you have seen in previous listings.

import java.util.*;

public class Generics05{
  ArrayList <Date> var1 = new ArrayList<Date>();

Listing 5

Populate the collection

Listing 6 shows the beginning of the runIt method.  This code populates the ArrayList object with references to three Date objects.  The first object encapsulates the current date and time.  The second object encapsulates the date and time one day later than the first.  The third object encapsulates the date and time two days later than the first object.

  void runIt(){
    //Get current date and time in milliseconds.
    long now = new Date().getTime();
    //Get length of one day in milliseconds
    long oneDay = 24 * 60 * 60 * 1000;

    //Populate the ArrayList object
    var1.add(new Date(now));
    var1.add(new Date(now + oneDay));
    var1.add(new Date(now + 2 * oneDay));

Listing 6

The code in Listing 6 is straightforward and shouldn't require further explanation.

Get and use an iterator

An iterator is an object instantiated from a specially designed class that implements the Iterator interface.  The design of the class makes it possible for client code to gain sequential access to each element encapsulated in an associated collection object without a requirement to know anything about how the collection is structured.

(If you are unfamiliar with the use of iterators and the Java Collections Framework, you can learn about those topics in my earlier lessons numbered 1350 through 1380.  You will find links to those lessons at http://www.dickbaldwin.com/tocint.htm.  Note, however, that those lessons were published long before the release of J2SETM 5.0, and therefore they do not explain generic iterators.  That is the purpose of this lesson.)

Required syntax

The first statement in Listing 7 shows the syntax required to get and save a reference to a generic iterator for the ArrayList object instantiated earlier in Listing 5.

    Iterator <Date> iter = var1.iterator();
    
    //Perform the iteration
    while(iter.hasNext()){
      System.out.println(iter.next());
    }//end while loop
    
    System.out.println();//blank line

Listing 7

Note the requirement to qualify the declaration of the local variable named iter with the type of data stored in the collection using the syntax <Date>.  You might think of this as a variable capable of holding a reference to an iterator object, which is capable of iterating on an ArrayList object, which in turn is capable of storing references to objects of type Date only.

Perform the iteration

The remaining code in Listing 7 uses the iterator to sequentially access and display a text representation of each of the three Date objects whose references are stored in the ArrayList object.  This is standard code for the use of an iterator and should not require further explanation.  This code produces the first three lines of text (plus the blank line) shown in Figure 7.

The program output

The output produced by this program depends on when you run it.  The output produced for one particular run is shown in Figure 7.

Fri Mar 11 06:29:38 CST 2005
Sat Mar 12 06:29:38 CST 2005
Sun Mar 13 06:29:38 CST 2005

Fri Mar 11 06:29:38 CST 2005
Sat Mar 12 06:29:38 CST 2005
Sun Mar 13 06:29:38 CST 2005
		
Figure 7

The output will be different each time you run the program depending on the current date and time.

The enhanced for loop

The code in Listing 8 performs the same iteration using the new enhanced for loop that was released in J2SETM 5.0.

    for(Date element : var1){
      System.out.println(element);
    }//end for-each

  }//end runIt

Listing 8

You might think of this syntax as meaning:

For each element of type Date contained in the collection referred to by var1, get the value of the element and save it in the variable named element.  Then use the contents of that variable to perform the operations specified within the body of the loop.

In this case, the only operation contained within the body of the loop is to display a text representation of the object referred to by the contents of the variable named element.

More compact syntax

As you can see, this approach does not require you to get an iterator and to explicitly use that iterator to sequentially access the elements in the collection.  Thus, the syntax is more compact than the syntax shown in Listing 7.  Further, by eliminating the requirement to get the iterator, this construct also eliminates the requirement for you to qualify the code using the <Date> syntax.  All of those details are handled automatically behind the scenes.

Not quite as powerful as an iterator

Although not shown in Listing 7, the use of an iterator allows you to remove the most recently accessed element from a collection.  As near as I can tell, the enhanced for loop does not provide that capability.  Therefore, the enhanced for loop is not quite as powerful as an iterator.  However, if you don't need to remove elements from the collection, the enhanced for loop appears to be a good and somewhat simpler alternative to an iterator.

The output

The code in Listing 8 produced the last three lines of text in the output shown in Figure 7.  Obviously the last three lines match the first three lines since they simply represent different ways to produce a text representation of the same three Date objects.

Use of the enhanced for loop with array objects

Although this has nothing to do with generics, I want to point out that the new enhanced for loop can also be used with Java array objects in addition to java collections.  This is illustrated by the program shown in Listing 9 (and also in Listing 16).

/*File Generics06.java 
Copyright 2005, R.G.Baldwin

Illustrates use of enhanced for loop with an
array object  Requires J2SE 5.0.  Depending on
when the program is run, the output should be 
similar to the following:

Fri Mar 11 08:08:37 CST 2005
Sat Mar 12 08:08:37 CST 2005
Sun Mar 13 08:08:37 CST 2005

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics06{
  
  //Create an array object suitable for 
  // storing references to Date objects.
  Date[] var1 = new Date[3];
  
  void runIt(){
    //Get current date and time in milliseconds.
    long now = new Date().getTime();
    //Get length of one day in milliseconds
    long oneDay = 24 * 60 * 60 * 1000;

    //Populate the array object
    var1[0] = new Date(now);
    var1[1] = new Date(now + oneDay);
    var1[2] = new Date(now + 2 * oneDay);

    //Iterate on the array object using the new
    // for-each construct.
    for(Date element : var1){
      System.out.println(element);
    }//end for-each

  }//end runIt

  public static void main(String[] args){
    new Generics06().runIt();
  }//end main
  
}//end class Generics06

Listing 9

Similar to Generics05

This program is very similar to the earlier program named Generics05.  The main difference is that the earlier program stored the Date object's references in a generic collection of type ArrayList.  This program stores the Date object's references in a simple array object of type Date.

The main point is that this program iterates on the array object using an enhanced for loop with the exact same syntax as that used in the previous program to iterate on a generic collection.  That code is highlighted in boldface in Listing 9.

What is the meaning of the code?

As before, you might think of the syntax of the boldface code in Listing 9 as meaning:

For each element of type Date contained in the array object referred to by var1, get the value of the element and save it in the variable named element.  Then use the contents of that variable to perform the operations specified within the body of the loop.

Otherwise, this program is completely straightforward, and I won't discuss it further.

Reverting back to pre-J2SETM 5.0 operation

I can imagine that the true believers in generics will consider what I am about to tell you now to be heresy.

May not want to use generics

Suppose that for reasons of your own, you don't want to take advantage of the improvements in compile-time type safety provided by generics.  For example, suppose that like many of us, you have lots of proven legacy code that is based on the Java Collections Framework.  The programs do exactly what they are supposed to do, and you have no desire to upgrade them to make them fully compliant with generics.

Tired of seeing compiler warnings

Suppose also that for reasons of your own you occasionally need to recompile those programs and you don't want to see compiler warnings produced by the J2SE 5.0 compiler with regard to generics every time you recompile.  An interesting question is,

"What is the easiest way for you to make the compiler quit complaining and to keep the behavior of your programs exactly the same as before?"

One possible solution

This solution may not be the best way to accomplish this objective.  There may be better ways, possibly using generic wildcards.  Also, true believers in generics will probably consider this to be poor programming practice.  However, I am going to show you one way to eliminate the compiler warnings and to keep the behavior of your programs exactly the same as before.

The program named Generics07

The program named Generics07 illustrates the syntax that can be used to restore an ArrayList collection to a state where it is capable of storing references to objects of mixed types.  This syntax also eliminates the generics-related compiler warnings that are produced by the J2SETM 5.0 compiler.

And the solution is ...

What you need to do is to qualify your collection-instantiation code using <Object> so that each of your collection objects can store references to objects of type Object only.  Because a reference to any object can be treated as type Object, you can then store references to objects of any type in the collection.  This is exactly how collections behaved prior to the advent of generics in J2SETM 5.0.

Don't forget to cast correctly

Having done this, you must remember that in order to invoke any method other than one of the eleven methods defined in the Object class on a reference that is retrieved from the collection, you must cast the retrieved reference to an appropriate type just as you did before J2SETM 5.0 was released.

Also remember that in doing this you have effectively disabled the compile-time type safety feature of generics.  It will be up to you to make certain that you cast correctly.

The program named Generics07

The program named Generics07 is shown in Listing 10 (and also in Listing 17 near the end of the lesson).

import java.util.*;

public class Generics07{
  
  ArrayList <Object> var1 = 
                         new ArrayList<Object>();
  
  void runIt(){
    var1.add(new Date());
    //Note the requirement to perform a cast in
    // the following print statements.
    System.out.println(
                  ((Date)var1.get(0)).getTime());
    var1.add("abcd");
    System.out.println(
                 ((String)var1.get(1)).length());
  }//end runIt

  public static void main(String[] args){
    new Generics07().runIt();
  }//end main
  
}//end class Generics07

Listing 10

Instantiate an ArrayList object

The boldface statement in Listing 10 instantiates an ArrayList object and saves that object's reference in the instance variable named var1.

Note that the expressions on both sides of the assignment operator are qualified using <Object>.  This, in effect, uses generics to produce a collection object that behaves the same way that collection objects behaved prior to J2SETM 5.0.  This also satisfies the compiler so that it won't issue the warnings discussed earlier.

Add and display a Date object

This ArrayList object is capable of storing references to objects of mixed types so long as all of the objects are instantiated from classes that are subclasses of the class named Object.  In Java, of course, all classes are subclasses of the class named Object.  Therefore, a reference to an object of any type, including type Date, can be added to the collection.

(Note, even references to array objects, such as references of type int[] can be stored in the ArrayList collection.)

The runIt method begins by instantiating a new Date object and adding its reference to the collection.  Then the code retrieves that reference from the collection, casts it to type Date, invokes the getTime method on the reference, and displays the value of the long integer returned by the getTime method.

Add and display a literal String object

Following this, the code in Listing 10 instantiates a literal String object that encapsulates the string "abcd" and adds that object's reference to the collection.  At this point, the collection contains a reference to an object of type Date and a reference to an object of type String.  Both of these references are stored in the collection as type Object.

Then the code in Listing 10 retrieves the reference to the String object, casts it to type String, invokes the length method on the reference, and displays the int value returned by the length method.

A cast is required

Once again let me emphasize that when you take this approach, you must cast a reference that is retrieved from the collection if you need to invoke any method on that reference other than one of the eleven methods defined in the Object class.  The type of the cast must be consistent with the original type of the object and the specific method that is to be invoked.  This is the way that collections behaved in the pre-generics days.

By taking this approach, you have effectively disabled the compile-time type safety feature of generics.  Therefore, it is up to you to make certain that your casts are correct.  Programs such as this will compile without warnings, and it is still possible to experience a ClassCastException at runtime if you perform an incorrect cast.

The program output

The output produced by one run of the program is shown in Figure 8.

1110553047835
4
		
Figure 8

The first line of text in the output depends on the current date and time, and will change from one run to the next.  The second line of text in the output should be 4 each time you run the program unless you modify the program to cause the String object to encapsulate a literal string of a different length.

Run the Programs

I encourage you to copy, compile and run the following programs that are provided in this lesson:

  • Generics01
  • Generics02
  • Generics03
  • Generics04
  • Generics05
  • Generics06
  • Generics07

You will find complete listings of these seven programs in Listing 11 through Listing 17.  Experiment with the programs, making changes and observing the results of your changes.

Have fun and learn

Above all, have fun and use these programs to learn as much as you can about the fundamentals of generics and the enhanced for loop in J2SETM 5.0.

Summary

In this lesson, I explained:

  • How the Java Collections Framework behaved prior to the release of J2SE 5.0.
  • The effect of an incorrect cast in code that doesn't use generics.
  • How to avoid the requirement to cast through the use of generics, including an illustration of some of the required syntax for generics.
  • The compile-time type safety provided by the use of generics.
  • The syntax requirements for the use of iterators with generics.
  • The use of the enhanced for loop with collections.
  • The use of the enhanced for loop with array objects.
  • How to achieve pre-generic behavior with a collection and also eliminate the compiler warnings.

What's Next?

Future lessons in this series will teach you about other new features in J2SE 5.0, including

  • Generic methods
  • Generic classes
  • Raw types in generics
  • Wildcards and bounded wildcards in generics
  • The impact of inheritance on generics
  • Autoboxing/Unboxing
  • Typesafe Enums
  • Varargs
  • Static Import

Complete Program Listings

Complete listings of all the programs discussed in this lesson are provided in Listing 11 through Listing 17 below.

/*File Generics01.java 
Copyright 2005, R.G.Baldwin

Illustrates requirement to cast without the use 
of generics.

V1.5 compiler produces following warning:
Note: Generics01.java uses unchecked or unsafe
operations.
Note: Recompile with -Xlint:unchecked for
details.

Recompilation with -Xlint:unchecked produces 
the following output:
Generics01.java:34: warning: [unchecked] 
unchecked call to add(E) as a member of
the raw type java.util.ArrayList
    var1.add(new Date());
            ^
1 warning

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics01{
  
  ArrayList var1 = new ArrayList();

  void runIt(){
    var1.add(new Date());
    //Note the required cast in the following
    // statement.
    System.out.println(
                  ((Date)var1.get(0)).getTime());
  }//runIt

  public static void main(String[] args){
    new Generics01().runIt();
  }//end main
  
}//end class Generics01
//=============================================//

Listing 11

/*File Generics02.java 
Copyright 2005, R.G.Baldwin

Illustrates the application of an incorrect cast 
to an element that is fetched from an ArrayList 
object and the runtime error produced by that
incorrect cast.

This program does not produce a compiler error, 
although the v1.5 compiler does produce a 
general warning having to do with the failure 
to apply the new generics syntax released in 
v1.5. (Earlier compilers would not have produced
such a warning.)

However, the compiler does not check to confirm 
that the correct cast is applied.  This results 
in the following runtime error when an incorrect 
cast is applied:

Exception in thread "main" 
  java.lang.ClassCastException: java.lang.String
        at Generics02.(Generics02.java:46)
        at Generics02.main(Generics02.java:53)

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics02{
  
  ArrayList var1 = new ArrayList();
  
  void runIt(){
    var1.add(new Date());
    var1.add("abcd");
    //Note that the (Date) cast is applied to 
    // an element of type String in the following
    // statement, producing a runtime error.  The
    // problem is that the wrong index was used
    // in fetching the element.  Thus, the wrong
    // element was fetched.
    System.out.println(
                  ((Date)var1.get(1)).getTime());
    System.out.println(
                 ((String)var1.get(1)).length());
  }//end runIt

  public static void main(String[] args){
    new Generics02().runIt();
  }//end main
  
}//end class Generics02

Listing 12

/*File Generics03.java 
Copyright 2005, R.G.Baldwin

Illustrates use of generics to avoid requirement 
to cast.  Requires v1.5 or later.

Program output for one run was:

1110504030101

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics03{
  
  ArrayList <Date> var1 = new ArrayList<Date>();

  void runIt(){
    var1.add(new Date());
    //Note that no cast is required in the
    // following statement.
    System.out.println(var1.get(0).getTime());
  }//end runIt

  public static void main(String[] args){
    new Generics03().runIt();
  }//end main
  
}//end class Generics03

Listing 13

/*File Generics04.java 
Copyright 2005, R.G.Baldwin

Illustrates ability of generics to prevent 
storing of wrong type in a collection.  
Requires v1.5 or later.

Compilation produces following error message:
Generics04.java:34: cannot find symbol
symbol  : method add(java.lang.String)
location: 
  class java.util.ArrayList<java.util.Date>
    var1.add("abcd");
        ^
1 error

Once the ArrayList has been declared to be of
type Date, it is not possible to add an element
of type String.  An attempt to do so produces
a compiler error.

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics04{
  
  ArrayList <Date> var1 = new ArrayList<Date>();
  
  void runIt(){
    var1.add("abcd");
    System.out.println(var1.get(0).getTime());
  }//end runIt

  public static void main(String[] args){
    new Generics04().runIt();
  }//end main

}//end class Generics04

Listing 14

/*File Generics05.java 
Copyright 2005, R.G.Baldwin

Illustrates required syntax for using an iterator
with generics.  Also illustrates the new for-each
construct in Java 5.0

Output for one particular run is shown below.

Fri Mar 11 06:29:38 CST 2005
Sat Mar 12 06:29:38 CST 2005
Sun Mar 13 06:29:38 CST 2005

Fri Mar 11 06:29:38 CST 2005
Sat Mar 12 06:29:38 CST 2005
Sun Mar 13 06:29:38 CST 2005

Output will be different each time the program
is run depending on the current date and time.

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics05{
  //Create an ArrayList object suitable for
  // storing references to Date objects.
  ArrayList <Date> var1 = new ArrayList<Date>();
  
  void runIt(){
    //Get current date and time in milliseconds.
    long now = new Date().getTime();
    //Get length of one day in milliseconds
    long oneDay = 24 * 60 * 60 * 1000;

    //Populate the ArrayList object
    var1.add(new Date(now));
    var1.add(new Date(now + oneDay));
    var1.add(new Date(now + 2 * oneDay));

    //Get an iterator
    Iterator <Date> iter = var1.iterator();
    
    //Perform the iteration
    while(iter.hasNext()){
      System.out.println(iter.next());
    }//end while loop
    
    System.out.println();//blank line
    
    //Now perform the same iteration using 
    // the new for-each construct.
    for(Date element : var1){
      System.out.println(element);
    }//end for-each

  }//end runIt

  public static void main(String[] args){
    new Generics05().runIt();
  }//end main

}//end class Generics05

Listing 15

/*File Generics06.java 
Copyright 2005, R.G.Baldwin

Illustrates use of enhanced for loop with an
array object  Requires J2SE 5.0.  Depending on
when the program is run, the output should be 
similar to the following:

Fri Mar 11 08:08:37 CST 2005
Sat Mar 12 08:08:37 CST 2005
Sun Mar 13 08:08:37 CST 2005

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics06{
  
  //Create an array object suitable for 
  // storing references to Date objects.
  Date[] var1 = new Date[3];
  
  void runIt(){
    //Get current date and time in milliseconds.
    long now = new Date().getTime();
    //Get length of one day in milliseconds
    long oneDay = 24 * 60 * 60 * 1000;

    //Populate the array object
    var1[0] = new Date(now);
    var1[1] = new Date(now + oneDay);
    var1[2] = new Date(now + 2 * oneDay);

    //Iterate on the array object using the new
    // for-each construct.
    for(Date element : var1){
      System.out.println(element);
    }//end for-each

  }//end runIt


  public static void main(String[] args){
    new Generics06().runIt();
  }//end main
  
}//end class Generics06

Listing 16

/*File Generics07.java 
Copyright 2005, R.G.Baldwin

Illustrates syntax required to restore an 
ArrayList collection to a completely generic
type capable of storing references to objects of
mixed types, while eliminating the compiler 
warning that is produced when an attempt is made
to use a Collection object in a completely 
generic sense with the v1.5 or later compiler.
This requires the use of the generic syntax to
declare the ArrayList to be of type Object.

Program output for one run was:

1110553047835
4

Tested using JDK 1.5 under WinXP.
************************************************/

import java.util.*;

public class Generics07{
  
  ArrayList <Object> var1 = 
                         new ArrayList<Object>();
  
  void runIt(){
    var1.add(new Date());
    //Note the requirement to perform a cast in
    // the following print statements.
    System.out.println(
                  ((Date)var1.get(0)).getTime());
    var1.add("abcd");
    System.out.println(
                 ((String)var1.get(1)).length());
  }//end runIt

  public static void main(String[] args){
    new Generics07().runIt();
  }//end main
  
}//end class Generics07

Listing 17


Copyright 2005, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel