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

Comments:

Dear Editors of Core Java Tech,
Do you know that < (&lt;) and >(&gt;) are special symbols in HTML? It seems yes, cause those symbols are escaped in code examples, except the second and the third ones.

I wonder how much time after generic implementation in JDK 5.0 would it take to implement a basic check (or, perhaps, a search and replace) for special chars in article before publishing?

WBR

Posted by Alexander Vasiljev on March 10, 2008 at 03:24 PM PDT #

You get
"can't convert from element type Object to AbstractButton"
on line:
"for (AbstractButton button: list) {"
Unless you change getLabels to:
"getLabels(List<AbstractButton> list)"

Posted by Martin on March 11, 2008 at 07:38 PM PDT #

Great post!! never knew about wildcards

Posted by Bhaarat on March 13, 2008 at 01:25 AM PDT #

Wouldn't it make more OO sense to have this behaviour by default? A bit like not having to declare methods/classes as 'virtual'?
Isn't Polymorphism a Java/OO cornerstone?
I'd much rather have to explicitly define a generic as e.g. <AbstractButton only> when I need such constraints!

Posted by Tasos Zervos on April 01, 2008 at 09:01 PM PDT #

public static List<String> getLabels(List7lt;AbstractButton> list) {
is a HTML coding problem with "<". Correct is:
public static List<String> getLabels(List<AbstractButton> list) {

Posted by guest on April 24, 2008 at 09:26 PM PDT #

sounds clear, but try the following example:

public static void main(String[] args) {
List<? extends Foo> list = new ArrayList<Foo>();
list.add(new Foo());
list.add(new Bar());
}
static class Foo {
}
static class Bar extends Foo {
}

why doesn't this compile?
why does it compile if i write
List<? super Foo>
???

Posted by Gordon Bartel on April 24, 2008 at 11:24 PM PDT #

I too agree with Tasos Zervos.
Usually its a default assumption by java programmers that when one method accepts class A as parameter, you can pass any subclass of A as an argument while calling method. So, it would be great if Generics too match with such behaviour. Thanks!

Posted by Pankaj Mandaliya on April 25, 2008 at 04:10 AM PDT #

I cant believe this. I cannot think how a wild card in generics fits into the java OOP theme. Upto now the rules were always simple and straight forward. In this case list object should have accepted all objects of sub classes by default. I feel generics in Java has made things complicated and confusing for the programmer.

Posted by MIK on April 25, 2008 at 03:19 PM PDT #

GOOD!

Posted by HLAYY on April 26, 2008 at 10:32 PM PDT #

To Tasos Zervos:

You cannot allow your suggestion in general, otherwise you could get problems, for instance, when defining

List <JButton> buttons = new ArrayList<JButton>();
List<AbstractButton> list = buttons;
list.add(new JMenuItem() );

This code now would be correct, but causes a JMenuItem to be inserted into a list of JButtons. By defining an <? extends AbstractButton> you can signal that you are aware of possible subclasses, but don't care.

Posted by Christian Koepke on April 27, 2008 at 05:24 PM PDT #

The generics syntax is a little ugly, but as people have pointed out, a good idea to help you be sure that you know what you're doing.

If you find yourself writing way too many ? extends constructs and think you shouldn't need to, you probably should re-think whether you actually want an abstract class or whether an interface would do the job for you.

It might have been nice to make "extends" the default and have "only" or "final" indicate that you really wanted just the named class. But that's a matter of preference.

(The whole issue comes up because of the type erasure mechanism of implementation; I've heard arguments that it shouldn't have been done this way, but I actually rather like it in contrast to the C++ method of making your code swell to massive proportions when you're using many different types, and I'm happy to avoid the extra overhead of automatic RTTI.)

Posted by Rex Kerr on April 28, 2008 at 03:13 AM PDT #

This generic stuff should be made default as MIK said. It is too confusing to keep it in this form in the codes.

Posted by Vineet Nair on April 29, 2008 at 12:40 AM PDT #

I would agree with the general sentiment. It's a general architectural assumption that going down the hierarchies in Java presents no type conflicts.

Having to add Container<? extends xxxxx>

Is very counterintuitive, and is not something I would have guessed could be done. This just adds syntactical convolution imo, and should be reduced to a hierarchical "ok"ness if the child class is inserted into a list typed as a parent.

The child class can after all be used as its parent! No?.....Yes!

Posted by Alex on April 29, 2008 at 02:27 AM PDT #

Looking at the comments so far, much of the confusion seems to stem from blurring the concepts of "defining" a class and "instantiating" a class of a given type.

When defining classes that accept generic types, they will automatically accept any sub-type of that generic type. For example, the Java List class is defined as "public interface List<E> extends Collection<E> { ... }"

GIVEN:
--------------------------------------------------------
class Fruit {}
class Apple extends Fruit {}
class Pear extends Fruit {}

THEN the following completely legal:
--------------------------------------------------------
List<Fruit> basket = new LinkedList<Fruit>();
basket.add(new Apple());
basket.add(new Pear());
Fruit a = basket.get(0);
Fruit b = basket.get(1);

However if you do something like this:
--------------------------------------------------------
List<Pear> pearBasket = new LinkedList<Pear>();
List<Fruit> fruitBasket = pearBasket;

The second line will not compile. This is because "List<Pear>" does \*not\* extend "List<Fruit>". (It's the List objects that are relevant here, not what the lists contain). If you want to create a List of Fruit objects from a List of objects which extend Fruit, then you must use:
--------------------------------------------------------
List<? extends Fruit> fruitBasket = pearBasket;

But then, fruitBasket.add(...) becomes illegal, because of the "type erasure"... The JVM does not know anything about the type constraints of the List (type-safety being solely the responsibility of the compiler). Since the runtime type checks don't happen, adding to a List<? extends Fruit> becomes illegal.

Though this might seem a bit awkward, I actually agree with the "type erasure" decision because Strong Typing is one of the best attributes of Java, IMO. Ideally, the compiler should not be allowed to generate any code which could cause a class cast exception... but that's another debate... :)

Posted by Adam Conover on April 30, 2008 at 06:51 AM PDT #

I encourage everyone who wants to really understand wildcards to read this paper by Bracha and Torgersen:
http://bracha.org/wildcards.pdf

I have summarized some useful tips about Generics here: http://sensualjava.blogspot.com/2008/01/taste-of-java-generics.html
and http://sensualjava.blogspot.com/2008/01/wildcards-observation.html

Regarding Tasos Zervos's suggestion of covariance by default - it may be trickier to implement, but I have read somewhere that as an afterthought both Peter Ahe (ex javac lead) and Gilad Bracha (ex GJ JSR lead) agreed that it should have been done.

P.S. The best on-line reference for programming with Java Generics: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

Posted by Yardena on April 30, 2008 at 04:44 PM PDT #

I think you should look into how Sather does constrained genericity. It's very much like this "? extends" business but is from years and years ago.

Sometimes everything old is new again?

Check also Eiffel to see its policy on covariance.

A shame these languages handled this all correctly years ago, but I'll bet most of you haven't heard of them.

Posted by Nathan on May 06, 2008 at 05:33 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

John O'Conner

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today