Using Generics With Wildcards and Extends

J2SE 5.0 introduced generics to the Java programming. 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. 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:

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

 

Share on Google Plus

About Veera

This is a short description in the author block about the author. You edit it by entering text in the "Biographical Info" field in the user admin panel.
    Blogger Comment
    Facebook Comment

0 comments:

Post a Comment