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 ofObjects, 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.
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:
"Hola, Mundo" is the Spanish translation of "Hello, World," according to Google. The results of
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 labels = getLabels(buttonList);
System.out.println(labels);
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 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;
}
}
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,