Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

JavaWorld Daily Brew

Using Generics With Wildcards and Extends

 

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 labels = getLabels(buttonList);
  System.out.println(labels);

"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&lt;AbstractButton&gt;) 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, see two earlier tutorials by
Gilad Bracha: a 2004
tutorial (PDF)
and the generics lesson in the online Java
Tutorial
.