So I have came across a weird compiling error when using a generic class that has a List (or Map or Set, etc) as an attribute.
The compiling error occurs while trying to iterate (using a foreach) the List:
Sample.java:11: error: incompatible types
for (String string : s.getStringList()) {
required: String
found: Object
Just to be clear, I know there's a simple workaround for this problem, but I want to understand what is wrong with the code
Following is the sample I have created:
import java.util.List;
public class Sample<T> {
public List<String> stringList;
public static void main(String[] args) {
Sample s = new Sample();
// Why this doesn't work?
for (String string : s.getStringList()) {
}
// Why does both of the following work?
List<String> newList = s.getStringList();
Sample<Object> s2 = new Sample<>();
for (String string : s2.getStringList()) {
}
}
public List<String> getStringList() {
return stringList;
}
}
These lines
Sample s = new Sample();
// Why this doesn't work?
for (String string : s.getStringList()) {
}
do not work because you are using the raw form of the Sample
class. When you use the raw form of the class, all generics in the class, even unrelated generics, have type erasure performed. That means that getStringList
now just returns a List
of Object
s, not a List<String>
of String
s.
This part of Java was introduced with Generics in Java 1.5 so that the old version of classes that now use generics would be backwards compatible. That way, something that iterates over List
, which had to use Object
before, still can use Object
by using the raw form of List
.
The JLS, Section 4.8 deals with raw types:
More precisely, a raw type is defined to be one of:
- The reference type that is formed by taking the name of a generic type declaration without an accompanying type argument list.
and
The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.
(emphasis mine)
The reasoning:
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 generics 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.
You're using the raw Sample
type, and the compiler thus assumes that you don't care about generic types, even for variables not declared as raw types inside this object. It's a strange rule, but it's how it is. You shouldn't use raw types if you care about generics. As soon as you use a raw type, all its members are also taken as raw types by the compiler.
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