Reputation: 1067
Why do collections that are not related to the template class drop their type? Here is an example: (Sorry, it will not compile because of the error I'm confused about.)
package test;
import java.util.ArrayList;
import java.util.List;
public class TemplateTest {
public static class A { }
public static class B<T extends Comparable> {
List<A> aList = new ArrayList<A>();
public List<A> getAList() {
return aList;
}
public int compare(T t, T t1) {
return t.compareTo(t1);
}
}
public static void main(String[] args) {
B b = new B();
for (A a : b.getAList()) { //THIS DOES NOT WORK
}
List<A> aList = b.getAList(); //THIS WORKS
for (A a : aList) {
}
}
}
This code throws an error upon compilation:
test/TemplateTest.java:24: incompatible types
found : java.lang.Object
required: test.TemplateTest.A
for (A a : b.getAList()) {
If I specify the template of B
like B<String>
, or if I remove the template from B completely, then everything is ok.
What's going on?
EDIT: people pointed out there was no need to make B generic so I added to B
Upvotes: 7
Views: 736
Reputation: 47954
Yes, it is known behaviour that if you use a raw type, then all type parameters on the class are lost, not just the type-level parameter that you failed to declare.
The issue is partly here:
If I specify the template of B like
B<String>
, or if I remove the template from B completely, then everything is ok.
That's not an option, you aren't to choose if you want to specify the type parameter or not. The only reason it compiles at all with no parameter specified is for backward compatibility. Writing new code with missing type parameters is a programming error.
List<A> list = b.getList()
does not successfully interpret the type, it is just effectively sticking in an arbitrary cast and trusting you that the assignment is correct. If you look at the compiler warnings it is in fact generating a warning for an unsafe conversion.
for(A a : b.getList()) {}
upgrades that warning to an error because the inserted cast would be inside compiler generated code, so it refuses to auto-generate unsafe code at all, rather than just give a warning.
From the java language specification:
The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.
Bottom line really is that the only significant thing java generics share with C++ templates is the <> syntax :)
More details: What is a raw type and why shouldn't we use it?
Upvotes: 8
Reputation: 27180
This is really similar to this bug:
6760983 : Unused type parameter triggering error in unrelated method
Which is reported here:
Unused Generic causing problem
Upvotes: 1
Reputation: 178253
First, in Java, it's Generics, not Templates like it is in C++.
You are declaring a generic type parameter T
in your class B
but you aren't using it. You should use T
instead of A
throughout your B
class defintion.
public static class B<T> {
List<T> aList = new ArrayList<T>();
public List<T> getAList() {
return aList;
}
}
Then you should use a type parameter when you declare your instance of class B
.
B<A> b = new B<A>();
But if you know that your aList
variable will always hold objects of type A
as the method name getAList
suggests, then there would be no reason to make class B
generic.
Upvotes: 1