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
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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With