Why A->B doesn't make List<A>->List<B>? Wouldn't that remove need for wildcards?

痞子三分冷 提交于 2019-12-08 01:36:13

问题


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.


回答1:


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!




回答2:


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);
    }
}



回答3:


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.




回答4:


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.




回答5:


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



来源:https://stackoverflow.com/questions/4229886/why-a-b-doesnt-make-lista-listb-wouldnt-that-remove-need-for-wildcards

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!