开发者

java contravariance doubt

I read this post about contravariance:

Let's now introduce another wildcard: ? super. Given a supertype B of a type A, then C<B> i开发者_Go百科s a subtype of C<? super A>:

List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits; 

but why C<B> is a subtype and not a supertype?


List<? super Apple means a list of some unknown supertype of Apple. In such a variable you can put a List<Apple>, List<Fruit> (assuming Fruit is a supertype of Apple) or List<Object>, for example.

This is exactly the meaning of supertype: List<? super Apple> is more general than List<Apple>.

  • Into a List<Apple> you can put apples, and nothing else. Everything you get out of the list is an apple.
  • Into a List<? super Apple> you can put apples (nothing else, since you don't know the exact type). Everything you get out of the list is an object, but we don't know what kind of object (since it could be a List<Object>, for example).

If we use List<? extends Apple>, it is similar: It means a list of some unknown subtype of Apple. This might be a List<Apple>, List<GoldenDelicious>, List<GrannySmith>, List<Jonagold> and so on.

  • Into a List<Apple> you can put apples (all types of apples), and everything you get out of it is an apple.
  • Into a List<? extends Apple> you can't put anything - as you don't know which subtype of Apple was actually used. But everything we get out of this is in fact an apple.

We can do all things and some more with a List<Apple> than with List<? super Apple> or with List<? extends Apple> - e.g. it is a subtype of both of these wildcard types.

(The same applies to all generic types and their wildcard variants, it is just more easy to see for lists and similar container types.)


Because that's how contravariance works. Consumer of fruits is a subtype of consumer of apples, because it can consume apples as well, thus it supports operations defined for consumer of apples (unlike producers that are covariant, i.e. producer of apples is a subtype of producer of fruits).

I.e. you should use contravariance when your List acts as a consumer, for example:

public void fillWithApples(List<? super Apple> basket) {
    basket.add(new Apple());
}

List<Apple> basketOfApples = ...;
fillWithApples(basketOfApples);

List<Fruit> basketOfFruits = ...;
fillWithApples(basketOfFruits);

List<Object> basketOfAnything = ...;
fillWithApples(basketOfAnything);


A useful rule for thinking about type hierarchies is the Liskov substitution principle. Basically, it says that A being a subtype of B means that you can use A anywhere that you can use B. It is generally the case that the converse doesn't hold.

For example, let's say i am an evil king who demands a tribute from his subjects in the form of a piece of Fruit. Can you bring me an instance of Apple? Yes, of course. So an apple is a subtype of fruit. Over the mountains, there is another evil king who demands tribute from his subjects in the form of Apples. Can you bring him a Banana, or some other piece of Fruit? No! He'll have you trampled to death by ponies if you do.

So, for you to live a long and happy life in my kingdom, you will need a supply of Fruit. You could keep this supply in a List. Could you keep it in a List<Fruit>? Yes, no problem. How about a List<Apple>? Fine, because i'll accept Apples. So for the purposes of production, List<Apple> is a subtype of List<Fruit>. Over the mountains in the land of the other evil king, of course, you can only have a List<Apple>, unless you want to spend a lot of time casting the contents of a List<Fruit> to Apples, which unless you're a wizard is pretty tiresome.

Now, after tribute day every year, i have a mountain of fruit. I don't even like fruit, so i have to put it somewhere. The obvious place is a List<Fruit>. Could i instead use a List<Apple> here? No! Because some of my dear subjects might bring me Bananas, and i would not be able to put those in such a list. However, over the mountains, a different situation obtains. The evil king there is only interested in Apples, so he can happily use a List<Apple> to store his nutritious hoard. But can he use a List<Fruit>? Yes! Because an Apple is a Fruit, so he can quite easily put them in such a list. It's going to be fiddly when he wants to get them out again, but he can put them in quite happily. So, for the purpose of consumption, we see that someone who can use a List<Apple> can also use a List<Fruit>, and so we have the perverse situation that List<Fruit> is a subtype of List<Apple>. This is why i don't go over to the other evil kingdom much. Funny place.

Still, it's better than the Haskell Republic.


Going by the rule "An instance of a subtype class sub should be assignable where ever an instance of class supertype is expected , it is pretty much evident.

Extend it further - Imagine an operation done to List<? super A> , - This operation will make sense on a List<B> , but not vice-versa.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜