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.
# 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.
# 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_
# 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>)
# 4
This one is completely wrong. B extends A doesn't imply List<B> extends List<A>.
# 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>?
# 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>.
# 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);
}
}
# 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>.
# 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...
# 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.
# 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.
# 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.
# 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.