X

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
.

Join the discussion

Comments ( 16 )
  • Alexander Vasiljev Monday, March 10, 2008

    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


  • Martin Wednesday, March 12, 2008

    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)"


  • Bhaarat Thursday, March 13, 2008

    Great post!! never knew about wildcards


  • Tasos Zervos Wednesday, April 2, 2008

    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!


  • guest Friday, April 25, 2008

    public static List<String> getLabels(List7lt;AbstractButton> list) {

    is a HTML coding problem with "<". Correct is:

    public static List<String> getLabels(List<AbstractButton> list) {


  • Gordon Bartel Friday, April 25, 2008

    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>

    ???


  • Pankaj Mandaliya Friday, April 25, 2008

    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!


  • MIK Friday, April 25, 2008

    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.


  • HLAYY Sunday, April 27, 2008

    GOOD!


  • Christian Koepke Monday, April 28, 2008

    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.


  • Rex Kerr Monday, April 28, 2008

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


  • Vineet Nair Tuesday, April 29, 2008

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


  • Alex Tuesday, April 29, 2008

    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!


  • Adam Conover Wednesday, April 30, 2008

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


  • Yardena Wednesday, April 30, 2008

    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


  • Nathan Tuesday, May 6, 2008

    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.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
Oracle

Integrated Cloud Applications & Platform Services