Compiler error only when calling method from WITHIN templated class

I can't figure out why this code won't compile:

class A{}

class Bextends A{}

class Test1<A>

{

void foo(A[] array){}

void bar()

{

foo(new B[10]);

}

}

class Test2

{

void bar()

{

new Test1<A>().foo(new B[10]);

}

}

The call to foo from Test2 succeeds. The call to foo from Test1 gives an error:

foo(A[]) in Test1<A> cannot be applied to (B[])

Why would the call location make a difference? It doesn't make sense to me.

[1300 byte] By [trevor@vocaro.coma] at [2007-11-26 16:22:17]
# 1
The main error here is that you misapplied Generics. The A in class Test1<A> is not the same as your class A, but a type parameter having the same name. Hence, B is not a subclass of that A.
stefan.schulza at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 2
You need to inistantiate Test1 correctly to be able to callfoo(new B[10])just like in Test2new Test1<A>().bar()should work.To evoid compile time error use cast clase. ((A[]) new B[10])Message was edited by: _Dima_
_Dima_a at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 3

Oops, you're right! It was a bad example. Here's a rewrite that should be a better illustration of the problem I'm facing:

import java.util.*;

class A {

void doSomething() {}

}

class B extends A {}

class Test

{

void foo(List<A> array)

{

array.get(0).doSomething();

}

void bar()

{

List<B> list = Arrays.asList(new B());

foo(list);

}

}

When I try to call foo, I pass in a List<B> type. B is a subclass of A, so B is compatible with A, and therefore List<B> is compatible with List<A>, right? But instead I get a compiler error:

foo(java.util.List<A>) in Test cannot be applied to (java.util.List<B>)

trevor@vocaro.coma at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 4
This one is completely wrong. B extends A doesn't imply List<B> extends List<A>.
_Dima_a at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 5

> This one is completely wrong. B extends A doesn't

> imply List<B> extends List<A>.

Why? It's perfectly valid for arrays:

class A {

void doSomething() {}

}

class B extends A {}

class Test

{

void foo(A[] array)

{

array[0].doSomething();

}

void bar()

{

B[] array = new B[1];

foo(array);

}

}

This compiles with no errors because B[] is compatible with A[].

So why is List<B> not compatible with List<A>?

trevor@vocaro.coma at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 6
List is a complex object while array is just wraper. Theoretically it could be that way but itis expensive and not needed indeed. If B extends A You always may use instance ofList<A>.
_Dima_a at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 7

> Theoretically it could be that way

Yes, intuitively it should work! That's why I was so confused.

> but it is expensive

I'm not sure what's so expensive about it. It's just a compile-time check, is it not?

> and not needed indeed.

But I need it!

I was able to work around the problem by wrapping Arrays.asList() in the A class:

import java.util.*;

class A {

void doSomething() {}

List<A> asList()

{

return Arrays.asList(this);

}

}

class B extends A {}

class Test

{

void foo(List<A> array)

{

array.get(0).doSomething();

}

void bar()

{

List<A> list = new B().asList();

foo(list);

}

}

trevor@vocaro.coma at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 8
It is not. All polimorphism is done in runtime.BTW it seems like functionality of Your samples quit different. But beleive me there can be only one List<A>.
_Dima_a at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 9
Dima,please could you help me with my assignment, which I'm sure that you will find easy? I'm desperate now! It's a User Interface...
nightprowlera at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 10

> It is not. All polimorphism is done in runtime.

Yes, polymorphism is at runtime, but type checking is not. The check that results in "List<B> is not compatible with List<A>" is done at compile time.

The polymorphism in this case occurs only when you pull an object from the list and call a function on it. But that also happens when arrays are used instead of Lists, so I don't think the incompatibility is due to runtime performance.

> BTW it seems like functionality of Your samples quit

> different.

No, it's the same.

trevor@vocaro.coma at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 11

> Why? It's perfectly valid for arrays:

Here's why:

List<A> lista = new List<A>();

List<B> listb = new List<B>();

listb = lista; // This is not allowed. Pretend it is for this example

lista.put(new A());

B b = listb.get(0); // This will retrieve an A from a list which claims to only hold B

Also, you really shouldn't use class names with a single character at the same time as generics - it gets very confusing very quickly.

dannyyatesa at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 12

B b = listb.get(0); // This will retrieve an A from a list which claims to only hold B

Ah, that's right! Good point.

But then why is this allowed?

class A {}

class B extends A {}

class Test

{

void foo(A[] arrayA)

{

arrayA[0] = new A(); // Throws ArrayStoreException

}

void bar()

{

B[] arrayB = new B[1];

foo(arrayB);

}

}

Shouldn't this also give a compile-time error?

Alternatively, the collections version should be compilable but then throw an exception (like the array version does) when you try to do this:

listb.put(new A())

At least then it would be consistent.

trevor@vocaro.coma at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...
# 13
Simple answer: arrays were less well specified than generics. Java arrays are well known to be non-typesafe for exactly the reason you give.
dannyyatesa at 2007-7-8 22:46:04 > top of Java-index,Core,Core APIs...