2

Disclaimer: I'm not a professional developer, and I'm not intending to become one. Reading book about Java, as I wanted to try Android programming, no previous Java experience whatsoever.

I'm reading this book - and I rather like it. I've read part of chapter about generic classes, got to the point where they mention wildcards, and got confused.

If B extends A:

  1. List<B> is not a subtype of List<A> (as I understand it they're exactly the same)
  2. List<? extends B> is a subtype of List<? extends A>

The latter allows for writing functions that accept arguments that are of generic type - for example List<? extends A>. Such function would accept an argument of either List<B> or List<A>.

Now, for my question:

Wouldn't it be simpler to implement generics in a manner similar to C++ (in a "template" flavour)? This would make List<B> and List<A> two separate types, that would be related in expected way. This would also allow to simply state in a function that you expect an argument to be of type List<A>, which would allow List<B> to fit there just fine.

I'm guessing there was more than "we hate C++, let's make things different" behind this :) It's also quite possible that I don't know something yet, that makes wildcards a fantastic and useful tool. What's your take on this?

Edit: if you're mentioning List<X> in your answer, remember to use backticks, to avoid <X> being interpreted as HTML tag.

yacoob
  • 811
  • 1
  • 11
  • 20

5 Answers5

5

There's a simple reason.

Suppose you have a variable of type List<A>. Suppose List<B> was indeed a subtype of List<A>.

That means that when this would be legal:

List<A> a_list;
a_list = new List<B>(); //allowed when List<B> is subtype of list<A>
a_list.add(new A()); // WOAH!

Where I say WOAH, the following happens: You add an item of type A to a_list. Since a_list was declared as List<A>, this should be legal. But wait: a_list is pointing to something of type List<B>.

So now we add something of type A to a list that should store only items of type B, and this is clearly not what we want, since A is not a subclass of B!

ColinD
  • 108,630
  • 30
  • 201
  • 202
cadolphs
  • 9,014
  • 1
  • 24
  • 41
  • And yet when you try to do this with arrays it compiles, then throws at runtime when you hit the WOAH! Phooey on covariant arrays. – Jeffrey Hantin Nov 19 '10 at 22:40
  • @Jeffrey: Arrays at least throw when you try to add something of the wrong type to them... generic constructs like a `List` won't throw until whenever you try to read something from them and cast it to something that it isn't. – ColinD Nov 19 '10 at 22:52
  • @Colin: That's the trouble with generics-by-type-erasure. Why the heck did they extend the JVM bytecode for assertions but not for generics? – Jeffrey Hantin Nov 19 '10 at 23:48
1

If List<B> was a subtype of List<A> the following code would be legal, as java does remove most of the Generic magic at compile time. (If that was a good decision or not is a different topic).

public void addNewAToList(List<A> list) {
    list.add(new A());
}


public static void main(String[] args) {
    List<B> listB = new LinkedList<B>();
    addNewAToList(listB);       // <--- compiler error
    for (B b : listB) {         // <--- otherwise: there is an A in the list.
        System.out.println(b);
    }
}
Hendrik Brummermann
  • 8,242
  • 3
  • 31
  • 55
  • First sentence of your answer probably have `List` - please put those into backticks, as SO thinks `` are HTML tags :) – yacoob Nov 19 '10 at 22:34
0

The problem is that if you have class C extends A and are given a List<A>, you can put a C in because a C is an A. But if Java allowed you to just give the method a List<B>, then you're putting a C in a list of Bs. And a C is not a B, so there'll be an error down the line. Java's wildcard solution doesn't let you add anything but null to a List<? extends A>, saving you from this mistake.

ColinD
  • 108,630
  • 30
  • 201
  • 202
0

Well, Comeau c++ online compiler fails on this:

template<typename T> class List {
};

class A {
};

class B: public A {
};

void function(List<A> listA) {
}

int main(int argc, char** argv) {
    List<A> a;
    List<B> b;

    function(a);
    function(b);
}

giving this error:

"ComeauTest.c", line 18: error: no suitable user-defined conversion from "List<B>" to
          "List<A>" exists
  function(b);

So C++ and Java are similar when dealing with these kinds of types.

gpeche
  • 21,974
  • 5
  • 38
  • 51
0

Static typing dictates that a subtype must support all operations of its supertype.

List<Fruit> supports inserting Fruit objects.

List<Banana> does not -- you cannot insert arbitrary fruits into a List<Banana>, only bananas.

Hence, List<Banana> is not a subtype of List<Fruit>.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662