In the Collections discussion we
noted that object containers expect to hold java.lang.Object
types and that the returned Object
types must be cast into the desired type. The new J2SE 5.0 includes
the very significant addition of generics feature in which
object containers can actually know what object type they
should hold. Then any attempt to insert an object of the wrong type
results in a compile-time error rather than a runtime ClassCastException.
Generics is a large an important addition to Java, and we only
scratch the surface of how to use generics. Generic features pervade
all the Collection Framework classes
as well as several other APIs in J2SE 5.0.
We use the ArrayList
class to illustrate the generics capability. Suppose we have a list
of strings
List list_of_strings
= new ArrayList ();
Without generics, we can insert any Object
type into the list - String,
Integer,
etc. With generics, we declare the type of object the list is allowed
to contain at compile time. The syntax is as follows:
List<String>
list_of_strings = new ArrayList<String> ();
The <String>
notation indicates that this particular List
object is only allowed to contain String
types. Any attempt to add an Integer
(autoboxed or not) or any other object type is disallowed at compile
time.
list_of_strings.add
("this is legal");
list_of_strings.add (new Integer (5)); // this is not allowed
list_of_strings.add (5); // neither is this
In the first illegal add(),
we explicitly wrap the 5
into an Integer.
Integer
is an Object
type and so could be added to a plain List,
but it is not permitted to be added to the List<String>
object container.In the second attempt, neither is the int
5 permitted. The compiler errors you will see look like this:
Generics.java:7:
cannot find symbol
symbol : method add (java.lang.Integer)
location: interface java.util.List<java.lang.String>
list.add (new
Integer (5));
^
Generics.java:8: cannot find symbol
symbol : method add (int)
location: interface java.util.List<java.lang.String>
list.add (5);
^
The compiler is saying that it cannot find an add()
method that takes an Integer
type in the interface java.util.List.
And it says there is not an add()
that takes an int.
By adding the generic type notation (also known as a
parameterized type) we have effectively created a new List
interface that permits only String
inserts. (Note that the second illegal add()
did not autobox the int
5 into an Integer.
Doing so would not have worked either since the first illegal attempt
already demonstrated that adding an Integer
is not permitted.)
Where generics becomes important and saves a lot of code is when
iterating over generic types and, in particular, in conjunction
with autoboxing and unboxing
of primitive types. Recall that without generics we must cast returned
Object types
into the desired type during an iteration. That was with J2SE 1.4
and below. With J2SE 5.0, the collection object "knows" what type
it holds, so it returns that type instead of the Object
type.
Without generics, iterating through a list of strings and printing
each out required code like this
List list_of_strings
= new ArrayList ();
...
for (Iterator it = list_of_strings.iterator (); it.hasNext ();)
{
String s = (String) it.next ();
Sytem.out.println (s);
}
But when
list_of_strings is a List type, this simplifies to
for (Iterator
it = list_of_strings.iterator (); it.hasNext ()) {
String s = it.next ();
Sytem.out.println (s);
}
We see that no explicit casting is needed.
With autoboxing and unboxing, we can insert and retrieve primitive
types without explicitly using the wrapper objects.
List<Integer>
list_of_ints = new ArrayList<Integer> ();
...
for (Iterator it = list_of_ints.iterator (); it.hasNext ();) {
int i = it.next ();
...
}
We should warn you of a nuisance with the J2SE 5.0 compiler. The
use of these special type-safe containers removes a significant
source of errors. For backward compatibility, all the old containers
are still available. So any pre-5.0 code continues to work exactly
the same in 5.0 and beyond. However, because the old container types
are potentially unsafe, the 5.0 javac compiler now issues warnings
about possible unsafe usage whenever it encounters one of the old
container types.
These are just warnings and can be ignored if you are sure that
the code is safe. The best way to get rid of the warnings is to
switch to the use of the generic types where appropriate.
Of course, sometimes the old non-type-safe containers are needed,
such as when you really want to insert a mix of object types. In
this case, you can just ignore the warnings. Alternatively, the
metadata (annotation) system discussed in Chapter
1: Supplements and Chapter
4: Supplements provides an @SuppressWarnings
annotation to explicitly suppress such warnings.
Generics and the Enhanced
For-Loop.
Note that generics allows for the enhanced for loop (or for-each
loop) discussed in Chapter
2: Supplements. to work with Collections containers. It offers
an elegant alternative to the Iterator
type loop in many cases. For example, the following snippet
ArrayList<string>
array_list = new ArrayList<string> ();
array_list.add ("Abc");
array_list.add ("Def");
array_list.add ("Ghi");
for (String str : array_list)
System.out.println (str);
gives an output as follows:
Abc
Def
Ghi
Specifying the ArrayList variable as
a String container means that the string
parameter in the loop will always be assigned the correct type.
References & Web Resources
Latest update: Oct. 19, 2005
|