tags:

views:

268

answers:

13

I have some confusion about generic programming in java:

If Manager is subclass of Employee,

Collection<Manager> managers=new Collection<Manager>;  
Collection<Employee> employees=managers;//why illegal?

Why the last statement is illegal?

Since according to illustration in the book CORE JAVA,after the erasion,Collection<Manager> and Collection<Employee> are all converted into raw type Collection.

+11  A: 

If the above were true, then you could take your collection of managers, treat it as a collection of employees, and then put any employee into a collection of managers (i.e. not just a manager, but a (say) graduate trainee, a CIO etc.)

It's a little counter-intuitive. An orange is a fruit. A list of oranges is not a list of fruit (otherwise you could put an apple in it). There's a concise explanation in the Java Generics Tutorial.

Brian Agnew
This is however, one of the differences between Scala and Java. In Scala, a Collection<Manager> is a subclass of Collection<Employee>.
MatthieuF
That's what wildcards for: Collection<? extends Employee> employees=managers; //now legalBut, for reason the Brian described, you can't call employees.add(). The parameter type just isn't specified. get() will work fine, as every subclass of Employee is an Employee, too.
Hardcoded
+2  A: 

Just imagine your code would be legal. (Let Apple be a subclass of Fruit)

Collection<Fruit> apples = { new Apple() };
Collection<Apple> fruit = apples;

fruit.add(new Banana()); // Inserting a banana into a list of apples
                         // Breaks type safety
Dario
Isn't this the other way around than the state he's showing?
abyx
@abyx: Now, but it used to be correct before the question was edited ;-)
Dario
A: 

If Employee is a subclass of Manager, Employee is more special, therefore

Collection<Employee> employees=managers;

is not legal.

It's like trying to get a collection of strings, but all you give the variable is a collection of objects ...

The MYYN
A: 

Forgetting about erasure for the moment: If you were allowed to assign a value of type Collection[Manager] to a variable of type Collection[Employee], then when you tried to, say add an Employee to the collection (which the variable name indicates is definitely legal), the addition would fail because what the variable is pointing to is actually a collection of Managers.

Java tries to keep you honest and consistent this way.

Carl Smotricz
A: 

Because anyone handling the employees collection will see it as simply being a collection of Employee, meaning there's no problem adding to it other Employee objects. But it's exactly the same instance as the one in the managers collection, meaning that someone handling the managers collection might get an Employee when expecting a Manager, thus ruining the whole concept of type safety.

abyx
A: 

if Y <-- X i.e. X i a type of Y, then you can substitue Y in place of X. However, that's not to say that a list of X is the same as a list of Y. This makes

Collection<Employee> employees=managers;

illegal

Aditya
+1  A: 

The following code snippet should highlight the problem.

Collection<Manager> managers=new Collection<Manager>;  
Collection<Employee> employees=managers;

employees.add(new Janitor());

// Writing it out with iterator instead of enhanced for-loop
for(Iterator<Manager> it = managers.iterator(); it.hasNext() ; ) {
    Manager manager = it.next(); // ! Here we'll crash with ClassCastException !
    System.out.println(manager.manages());
}

So, it is not the employees list that is the problem - it is that the managers list can now have other stuff shoved into it.

You can force it through by casting if you want - but then you probably buy yourself trouble later.

Java's generics guarantee that you will NEVER get such a ClassCastException anywhere IFF you have not made any such errors or warnings.

As a side-note, the exact same situation is actually possible/legal with arrays, and as such can give you ArrayStoreExceptions where you would not expect it.

Manager[] managers = new Manager[20];
Employee[] employees = managers; // No error or warning

employees[15] = new Janitor(); // gives ArrayStoreException
stolsvik
A: 

If you don't want to change your declaration of employees, you can do it like this:

Collection<Employee> employees=new ArrayList<Employee>(managers);
True Soft
A: 

Actually, what you're trying is possible:

Collection managers = new Collection<Manager>(); // note the missing generic type on the variable
Collection employees = new Collection<Employee>();

employees = managers; //compiles fine.

You do get some generic type warnings, though. But as long as you don't cast your collection back to one of type Manager or Employee, you're fine. And you will only be able to get Objects from your collection.

Jorn
+3  A: 

This is called covariance and contravariance. The issue you just discovered is a traditional problem of type systems. The corresponding wikipedia page discusses this issue in Java. It's worth taking some time to understand these issues correctly has it also relates also to method overriding (since JDK5, you can indeed change the return type of an overriden method as long as it's covariant).

ewernli
A: 

Why the last statement is illegal?

Since according to illustration in the book CORE JAVA,after the erasion,Collection and Collection are all converted into raw type Collection.

Because generics in java are keeping your code safe during build time. If you want this to work, use wildcard expressions (extends or super).

HappyCoder
A: 

If Manager extends Employee, you can fix your code as follows:

Collection<Manager> managers=new Collection<Manager>;  
Collection<? extends Employee> employees=managers;//now legal

The ? is known here as the wildcard symbol. You can find explanations in Using and Programming Generics in J2SE 5.0.

paradigmatic
A: 

if you write:

Collection<MYObject> managers=new Collection<MYObject>;  
Collection<Object> employees=managers;

work because MYObject is child of Object.

if Manager is child of employee that work.

tommaso
So you haven't tested it?
BalusC