By John Zukowski
Java 2 Platform, Standard Edition 5.0 (J2SE 5.0) introduced
generics to the Java programming language and platform. In the
simplest case and typical usage, generics allow for the
identification of what you want to store in a collection. So instead
of saying that your program has a List
of
Objects
, you can now specify that you have a
List
of String
objects or some other class
type. Then, if you accidentally try to add something to the
List
that is of the wrong type, the compiler notifies
you of the error and it can be fixed at compile time, rather than
having to wait until you run the program and the program reaches the
point in code where the fetch operation produces a runtime casting
exception.
This brings up a second benefit of generics. Iterators now become
typesafe. The next()
method of the
Iterator
interface returns the typesafe version of the
next element of the collection.
But this is not a tip about the use of generics, which a 2005 Core Java Technologies Tip explained. Most
people don't fully understand the use of the extends
keyword when using generics. A typical example shown to explain the
use of extends
has to do with drawing shapes. Instead,
this tech tip will use an example that uses Swing components so that
you do not have to create extra new classes. In a very limited case,
the class hierarchy for Swing button components is shown here, with
Object
as the real root:
Component
|- Container
|- JComponent
|- AbstractButton
|- JButton
|- JMenuItem
|- JCheckBoxMenuItem
|- JMenu
|- JRadioButtonMenuItem
|- JToggleButton
|- JCheckBox
|- JRadioButton
One thing that all AbstractButton
subclasses share
in common is a getText()
method. So in the spirit of
generics, you can define a method that takes a List
of
AbstractButton
items and return a List
of
the String
labels of those buttons. Here's the first
version of such a method:
public static List<String> getLabels(List<AbstractButton> list) {
List<String> labelList = new ArrayList<String>(list.size());
for (AbstractButton button: list) {
labelList.add(button.getText());
}
return labelList;
}
And here is how you might use the method. First, define a
List
of AbstractButton
types, fill it up,
and call the method:
List<AbstractButton> buttonList =
new ArrayList<AbstractButton>();
buttonList.add(new JButton("Hello"));
buttonList.add(new JCheckBox("World"));
buttonList.add(new JRadioButton("Hola"));
buttonList.add(new JMenuItem("Mundo"));
List<string> labels = getLabels(buttonList);
System.out.println(labels);
</string>
"Hola, Mundo" is the Spanish translation of "Hello, World,"
according to Google. The results of the println()
call
is as follows:
[Hello, World, Hola, Mundo]
With a List
of AbstractButtons
,
everything functions fine, but this breaks down when the
List
is of something else, specifically a subclass. One
would logically think that with a List
of
JButton
items, everything would work fine, because
JButton
is a subclass of AbstractButton
.
Shouldn't you be able to call
getLabels(List<AbstractButton>)
with a
List
of a subclass of AbstractButton
?
List<JButton> buttonList = ...
// ... Fill list ...
List<String> labels = getLabels(buttonList);
Well, that isn't the case. Because this is a compile-time check,
and because the definition of getLabels()
is defined to
accept only an AbstractButton List
, you cannot pass
anything else to it. The compilation-time error message follows:
GetList.java:13: getLabels(java.util.List<javax.swing.AbstractButton>)
in GetList cannot be applied to (java.util.List<javax.swing.JButton>)
List<String> labels = getLabels(buttonList);
^
1 error
And this is where the extends
keyword comes in
handy. Instead of defining the getLabels()
method to
accept only an AbstractButton
list, define it to accept
any List
of AbstractButton
subclasses:
public static List<String> getLabels(
List<? extends AbstractButton> list) {
The wildcard ?
here says that the method doesn't
care what the exact class type is, as long as it is a subclass of
AbstractButton
. Here's a complete example that puts all
the pieces together:
import java.util.*;
import javax.swing.*;
public class GetList {
public static void main(String args[]) {
List<JButton> buttonList =
new ArrayList<JButton>();
buttonList.add(new JButton("Hello"));
buttonList.add(new JButton("World"));
buttonList.add(new JButton("Hola"));
buttonList.add(new JButton("Mundo"));
List<string> labels = getLabels(buttonList);
System.out.println(labels);
}
public static List<String> getLabels(
List<? extends AbstractButton> list) {
List<String> labelList = new ArrayList<String>(list.size());
for (AbstractButton button: list) {
labelList.add(button.getText());
}
return labelList;
}
}
</string>
Now, when you are defining your own classes and methods with generics and are thinking of accepting an abstract class as the generic argument, or any superclass, remember to use wildcards so that the same method works best with subclasses too.
For more information on generics, see two earlier tutorials by Gilad Bracha: a 2004 tutorial (PDF) and the generics lesson in the online Java Tutorial.
This story, "Using Generics With Wildcards and Extends" was originally published by JavaWorld.