JavaGenerics in J2SE 5.0

Generics in J2SE 5.0

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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 ArrayList
ArrayList 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-

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories