Consider the following code:
import java.util.Arrays;
import java.util.List;
class Person {}
class OtherPerson {}
class SomeDummyClass {}
interface A {
<E>List<E> foo();
}
class B implements A {
@Override
public List<Object> foo() {
return Arrays.asList(Integer.valueOf(3), new Person(), new OtherPerson());
}
}
public class Main {
public static void main(String[] args) {
A a = new B();
List<SomeDummyClass> someDummyClasses = a.foo();
System.out.println(someDummyClasses.get(0)); // prints 3
}
}
I'm confused why we can override the foo()
method without any compile time errors. This particularly became less intuitive to reason about when I wrote List<SomeDummyClass> someDummyClasses = a.foo();
and this is perfectly valid (no compile time error).
A real time example of this case is with mybatis-3
.
A
mimicsResultHandler
B
mimicsDefaultResultSetHandler
(This class is not package private - which I expected it should have been)foo()
mimicshandleResultSets
Questions
- I understand that why is this statement:
List<SomeDummyClass> someDummyClasses = a.foo();
not giving any compile time error. But at runtime, I thought it should have given aClassCastException
. What's actuall happening here? - When I do
System.out.println(someDummyClasses.get(0));
I get aClassCastException
. Why is this so? - Is there a better way to re-write these so that the code becomes less fragile?
I'm confused why we can override the foo() method with any compile time errros.
Yes but you have a compilation warning since the return type in the overrided method List<Object>
is more specific that List<E>
in the interface method :
Unchecked overriding: return type requires unchecked conversion. Found
java.util.List<java.lang.Object>
, requiredjava.util.List<E>
.
To not handle this kind of warning may lead to an inconsistent use of the generics. And there you have a very good example.
About :
This particularly became less intuitive to reason about when I wrote List someDummyClasses = a.foo(); and this is perfectly valid (no compile time error).
And why would you get a compilation error ?
Here you declare a
as a A
:
A a = new B();
So here :
List<SomeDummyClass> someDummyClasses = a.foo();
at compile time, foo()
refers to the <E> List<E> foo()
method declared in the interface and a method scoped generic with E
that is not bounded (so is Object
) is designed to adapt the declared return type to the target : here that is the type of the variable declared by the client of the method to assign the method return.
Change your declaration to :
B b = new B();
List<SomeDummyClass> someDummyClasses = b.foo();
And the compilation would not even pass.
Note that your issue is not specific to overriding method. Declare a method (that also produces a warning about unchecked cast) :
public static <T> List<T> getListOfAny(){
return (List<T>) new ArrayList<Integer>();
}
And you could use it in the same inconsistent way :
List<String> listOfAny = getListOfAny();
The moral of this story : don't consider unchecked conversions warnings produced by the compiler like cosmetics/details but handle them correctly and you could benefit as much as possible from the compilation check brought by generics.