If you declare an instance of a generic class as a raw type in Java, does the compiler assume a parametrized type of Object
for all class member methods? Does this extend even to those methods which return some form (e.g. a Collection
) of a concrete parametrized type?
I erroneously declared an instance of a generic class without a parametrized type, which led to very interesting downstream effects. I've provided a simplified example of a scenario which produced an 'incompatible types' compilation error. My basic question: Why does javac produce an 'incompatible types' error, complaining about the indicated line below?
import java.util.*;
public class Main {
public static void main(String[] args) {
final Test<String> t1 = new Test<String>();
final Test t2 = new Test<String>();
for (final Integer i : t1.getInts()) {
System.out.println(i);
}
for (final Integer i : t2.getInts()) { //<-- compile-time error
System.out.println(i);
}
}
public static class Test<T> {
public Test() {
}
public Set<Integer> getInts() {
final Set<Integer> outSet = new HashSet<Integer>();
outSet.add(new Integer(1));
outSet.add(new Integer(2));
outSet.add(new Integer(3));
return outSet;
}
}
}
An interesting note is that if t2
is declared with a wildcard type (Test<?> t2…
) the code compiles and runs as expected.
The use of the for-each loop invokes next()
on the iterator on the set returned by getInts()
(which returns a generic type gleaned from the Iterable being iterated over). The documentation here mentions that if the Iterable is raw, Object
is used as the return type. Why does the compiler also seem to change the return type of Test.getInts()
from Set<Integer>
to Set<Object>
?
The generic class works with multiple data types. A normal class works with only one kind of data type.
Implementing generics into your code can greatly improve its overall quality by preventing unprecedented runtime errors involving data types and typecasting.
The question mark ( ? ) wildcard character can be used to represent an unknown type using generic code. Wildcards can be used with parameters, fields, local variables, and return types.
It inherits all members defined by super-class and adds its own, unique elements. These uses extends as a keyword to do so. Sometimes generic class acts like super-class or subclass. In Generic Hierarchy, All sub-classes move up any of the parameter types that are essential by super-class of generic in the hierarchy.
Because you used a raw Test
, all generics in the Test
class are effectively type-erased, even unrelated generics such as returning Set<Integer>
, so that the Set<Integer>
returned by getInts
becomes just a raw Set
. That explains why you get incompatible types, because a raw Set
will return Object
s, not Integer
s.
Section 4.8 of the JLS covers raw types:
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.
and
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.
(emphasis mine)
The reason for this behavior is for backwards compatibility with pre-generics Java code, because the pre-generics versions of built-in Java classes are the same things as the raw type versions of the generified built-in classes. That is, a pre-generics Set
is the same thing as a raw Set
(generics removed).
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