Generics in Java
Asked Answered
P

4

2

I came across something peculiar today. Take a look at this code snippet:

List <Rectangle> test1 = new LinkedList<Rectangle>();
List <Shape> test2 = test1; //Compiler Error;

This is of course is assuming that class Rectangle is a subclass of Shape. Can someone explain to me why this is an error?

Purr answered 25/11, 2009 at 5:27 Comment(0)
G
4

If this code worked, you could continue by inserting into test2 a Circle -- thus utterly breaking the guarantee that test1 makes, that only Rectangles will ever be inserted in it.

The general principle (language independent -- a matter of logic -- even though counter-intuitive): a bag of bananas is NOT a bag of fruit... in a world of mutable objects (the functional programming world, where every object is immutable once created, is MUCH simpler!). That's because you can add an apple to a bag of fruit (since an apple is a piece of fruit), but you can't add an apple to a bag of bananas (since an apple is not a banana).

BTW, this is very similar to the reason a square is not a rectangle (again, in a world of mutable objects): because given a (mutable) rectangle you can mutate the two sides independently, but, given a square, you can't. (In geometry, a square IS indeed a rectangle - but that's because, in geometry like in functional programming, there is no concept of "mutating" an object!-).

Ginnie answered 25/11, 2009 at 5:34 Comment(0)
W
5

You need to use wild card. Like this:

List<Rectangle> test1 = new LinkedList<Rectangle>();
List<? extends Shape> test2 = test1;

As Rectangle extends Shape.

The reason for this is that if test2 is just List<Shape>, you will expect test2.add(..) to accept any shape but that is not the case if you allows test1 (which is List<Rectangle>), test1 will not accept any shape that is not Rectangle.

Hope this helps.

Woodward answered 25/11, 2009 at 5:34 Comment(0)
G
4

If this code worked, you could continue by inserting into test2 a Circle -- thus utterly breaking the guarantee that test1 makes, that only Rectangles will ever be inserted in it.

The general principle (language independent -- a matter of logic -- even though counter-intuitive): a bag of bananas is NOT a bag of fruit... in a world of mutable objects (the functional programming world, where every object is immutable once created, is MUCH simpler!). That's because you can add an apple to a bag of fruit (since an apple is a piece of fruit), but you can't add an apple to a bag of bananas (since an apple is not a banana).

BTW, this is very similar to the reason a square is not a rectangle (again, in a world of mutable objects): because given a (mutable) rectangle you can mutate the two sides independently, but, given a square, you can't. (In geometry, a square IS indeed a rectangle - but that's because, in geometry like in functional programming, there is no concept of "mutating" an object!-).

Ginnie answered 25/11, 2009 at 5:34 Comment(0)
T
3

This is a common source of confusion - generics are not covariant in Java.

For a really good explanation of this please see Java theory and practice: Generics gotchas:

While you might find it helpful to think of collections as being an abstraction of arrays, they have some special properties that collections do not. Arrays in the Java language are covariant -- which means that if Integer extends Number (which it does), then not only is an Integer also a Number, but an Integer[] is also a Number[], and you are free to pass or assign an Integer[] where a Number[] is called for. (More formally, if Number is a supertype of Integer, then Number[] is a supertype of Integer[].) You might think the same is true of generic types as well -- that List is a supertype of List, and that you can pass a List where a List is expected. Unfortunately, it doesn't work that way.

Tuinenga answered 25/11, 2009 at 5:29 Comment(0)
H
0

List<Shape> has nothing to do with List<Rectangle>. When you are using an array, Shape[] is actually supertype of Rectangle[], because arrays are covariant. But Collections such as List are not covariant. In fact at run time, they will both be erased as List<Object>, and when you call any methods from any instances of List<Shape>, List<Rectangle>, the VM will call the methods of List<Object> but with some other code inserted to check the type safety.

Hannon answered 12/7, 2013 at 18:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.