4

I have read many sources about wildcards and Java generics. Even though I have seen many explanations and answers, none of them seems correct.

The question is very simple: Do wilcards in Java generics increase flexibility?

My understand is "No". Wildcards in Java generics (wilcards) actually decrease flexibility. When I first looked at and used Java generics mechanism, I rarely/never used wildcards. Very often I saw and used the form <T> abc(T xt). One day I got a test question about super and later I started looking at wildcards more closely.

Many sources and in particular: Effective Java 2nd gives an example as follows to show how wildcards increase flexibility.

public class Stack<E> {
    ...
    public void pushAll(List<E> list) {...}
    ...
}

The books goes on and says that since we should be able to use List of anything extending E as argument to pushAll(List<E> list), this is not flexible enough. Therefore, the book suggest changing the signature of the method to pushAll(List<? extends E> list). Now if E is Number then we can even use List<Integer> as argument for pushAll(List<? extends Number> list). And that we have increased flexibility (of the API).

My understanding is that the other solution is not to use wildcards but to use the regular typing. In that case, if we change the method signature to

public <T extends E> void pushAll(List<T> list)

we will achieve the same thing that the wildcard version wants to achieve. And now in the body of the method, not only we can read from list, we can also write into it. If we use the the wildcard version, we cannot write into that list any value other than null. So in this view, wildcards actually decrease flexibility, not increase.

All in all, it looks to me that wildcards are actually used to decrease flexibility so that <? extends X> discourages people from writing into the object while <? super X> discourages people from reading the object (try it and you will get only Object as type of the returned value). The following example illustrates the <? super X> case.

public void popAll(List<? super E> list) {
    Object x = list.get(0) // Only get Object as type here, reading is discouraged
}

We can change the signature to public <T extends E> void popAll(List<T> list). Now we can write or read as we want. There is no restriction.

So to sum up: All usages of wildcards can be substituted by typed parameter in the manner of <T extends E> (so there can be no need for the keyword super) . Using wildcards, we lose flexibility in the ability to both read and write into the object. Therefore, wildcards decrease flexibility.

Is it therefore true that wildcards in Java actually decrease flexibility? If not, how is my reasoning flawed?

[My question is flawed as per missing the cases as mentioned by @Doval in the comment under his answer.]

10
  • They increase flexibility. They are useful to accept code you cannot accept with normal type parameters in java. Example would be List<List<T>> is not as permissive as List<List<?>>. en.wikipedia.org/wiki/… Commented Feb 5, 2015 at 5:37
  • @Thomas Eding But List<List<?>> is not flexible (cannot insert other than null) as <E extends Object> List<List<E>>. The latter one is equally permissive.
    – InformedA
    Commented Feb 5, 2015 at 6:01
  • 1
    "But List<List<?>> is not flexible" you've got it the wrong way around. The more restrictions you put on the implementer, the more freedom you're giving the caller. The fact that the implementer can't make any assumptions about the contents of the inner Lists means the caller can give them any list-of-lists. That aside, I don't know where you're getting this idea that you can substitute ? super E with T extends E.
    – Doval
    Commented Feb 5, 2015 at 14:14
  • @Doval Try it yourself and see where the idea is from.
    – InformedA
    Commented Feb 5, 2015 at 15:07
  • 2
    Yes, and you can change ? super E for T super E, but not for T extends E. super != extends. That is my point.
    – Doval
    Commented Feb 5, 2015 at 15:39

2 Answers 2

7

My understanding is that the other solution is not to use wildcards but to use the regular typing. In that case, if we change the method signature to (...) And now in the body of the method, not only we can read from list, we can also write into it. If we use the the wildcard version, we cannot write into that list any value other than null. So in this view, wildcards actually decrease flexibility, not increase.

The problem is that you're assuming the increased flexibility is for the person writing the function; it's for the person using the function. The more flexibility the implementation gives up, the more flexibility the caller gains. The restrictions on the implementation are guarantees for the caller.

Case in point, when you change pushAll(List<E>) to pushAll(List<? extends E>), the implementer can no longer assume that he knows the precise type stored in the list, and that is why you can't safely write to the list. But because of that, the caller is now free to to pass any list whose elements are a subtype of E, whereas before he could only pass lists of E.

You claim that if you change pushAll to <T extends E> void pushAll(List<T>) you can write to the list, but that's not true in this case. You don't know what class T is ahead of time, so you can't possibly create a T to insert into the list. The only way you could possibly add a T to the list is if the caller gave it to you. That's why Effective Java argues that you should replace type parameters with wildcards if they only appear once in the method; the main purpose of type parameters is to allow you to give an unknown type a name so you can refer to it more than once.

All in all, it looks to me that wildcards are actually used to decrease flexibility so that discourages people from writing into the object while <? super X> discourages people from reading the object (try it and you will get only Object as type of the returned value). (...) We can change the signature to public <T extends E> void popAll(List<T> list). Now we can write or read as we want. There is no restriction.

You can't just swap super for extends. Let's say you have a Stack<Number>. What's going to happen if you pass a List<Integer> to popAll and the stack tries to insert a Double into the list? Likewise, suppose you pass a List<Object> to pushAll; what's going to happen if the list has a String and you try to insert it into a stack of numbers? Bad things. That's why you use ? extends E when you want to read from the collection and ? super E when you plan on writing to it; it's the only safe way to do it. (There's a mnemonic for remembering which to use: PECS. Producer extends, consumer super.)

Besides, type parameters can't have lower bounds (you can't say T super SomeClass) so you're forced to use a wildcard for super.

4
  • This is just so wrong. But I will just tell you about the last sentence you have. You can just use SomeClass instead of ? super SomeClass, and you can write just fine PLUS you can also read more than just the methods provided in Object.
    – InformedA
    Commented Feb 7, 2015 at 6:52
  • 1
    @InstructedA If you make that change then your function becomes a whole lot less useful. Now it only accepts Foo<SomeClass>, while before you could use any of SomeClass's superclasses as well.
    – Doval
    Commented Feb 7, 2015 at 19:15
  • 2
    Uh ohh you are right. .
    – InformedA
    Commented Feb 8, 2015 at 5:57
  • stackoverflow.com/questions/19585660 shows the restriction to same interfaces twice.
    – Grim
    Commented Dec 2, 2015 at 13:14
3

Item 28 of the good book (Effective Java 2e) is 'Use bounded wildcards to increase API flexibility'.

Yes bounded wildcards increase flexibility over type signitures without bounds, but they lack the ability to express some concepts that can achieved with non-wildcard type bounds.

Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code.

So when a type appears in the return type, it must not be a wildcard. It is also best not to wildcards if a type appears multiple times in the type signiture.

So why would you ever want bounded wildcards?

There is a duality between type parameters and wildcards, and many methods can be declared using one or the other. [...] As a rule, if a type parameter appears only once in a method declaration, replace it with a wildcard.

They can be easier for people using your API to read.


However, Effictive Java 2E was written for Java5, which was a while back, and Java7 came with improvements to the inference for type parameters and the diamond operator. Perhaps in this more modern age type parameters should be prefered.

1
  • Wildcards are still more obvious to someone reading the documentation Commented Feb 5, 2015 at 21:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.