I don't understand the compiler error resulting from the following code. I define a generic interface, see Task, with two methods: U doSomething(String value)
and List<Integer> getIDs()
. The doSomething() method actually uses the generic type as the type of its return value, but doesn't seem to be causing problems. The getIDs()
method returns a List, which is unrelated to the type of Task, but it is causing problems when using for..each statement to iterate over the return value. The following compiler error occurs.
error: incompatible types for (Integer value : task.getIDs()){ required: Integer found: Object
It seems that the type erasure on the interface is causing the compiler to forget the declared type on the second method, which is unrelated to the generic type. Or in other words why is the generic type on the interface affecting how the compiler understands the return value on the getIDs()
method and specifically in the context of a for..each statement?
Apparently if I get reference to the list outside of the for..each there is no problem, but not directly.
public class InterfaceTest {
public static void main(String[] args) {
Task task = new MyTask();
// no complaints about the type here
List<Integer> values = task.getIDs();
// getting a compiler error for this line
for (Integer value : task.getIDs()){
}
}
}
interface Task<U>{
U doSomething(String value);
List<Integer> getIDs();
}
The implementation of the interface isn't necessary to demonstrate the point, but I didn't want to leave the reference Task task = null;
and have answer's telling me that's the problem.
class MyTask implements Task<Boolean>{
@Override
public Boolean doSomething(String value) {
System.out.println(value);
return false;
}
@Override
public List<Integer> getIDs() {
return Arrays.asList( 1, 2, 3, 4 );
}
}
remove() (in Map as well as in Collection ) is not generic because you should be able to pass in any type of object to remove() . The object removed does not have to be the same type as the object that you pass in to remove() ; it only requires that they be equal.
Java Generic Interface In similar way, we can create generic interfaces in java. We can also have multiple type parameters as in Map interface. Again we can provide parameterized value to a parameterized type also, for example new HashMap<String, List<String>>(); is valid.
Generic MethodsAll generic method declarations have a type parameter section delimited by angle brackets (< and >) that precedes the method's return type ( < E > in the next example). Each type parameter section contains one or more type parameters separated by commas.
What is happening is when use use a class (or interface) with a generic parameter <T>
but refer to and instance of the without <T>
(ie. that raw
type) the compiler erases all generic type information from the class. This is likely due to compatibility with pre-1.5 source code where you wouldn't be able to use generic type information at all.
Consider the situation where you are writing code and compiling on a Java 1.4 compiler. You want to use a library which makes use of generics. When you refer to a type from that library which has generic parameters as a raw type, the compiler enforces the use of no generic parameters.
EDIT:
The JLS-4.8-210 alludes to this when it mentions (credit: zhong-j-yu):
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.
This still feels like a gotcha, but it is likely for some reason.
The error seems to lie here:
Task task = new MyTask();
You have forgotten to add generics after Task
. It should work if you change it to one of these:
Task<Boolean> task = new MyTask();
Task<?> task = new MyTask();
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