Given the following interface and class:
public interface Interface<T> {
List<T> get(List<List<Object>> keys);
}
public class Cls implements Interface {
@Override
public List get(List<List<Object>> keys) {
return Collections.emptyList();
}
}
Results in a compile error:
TypeTest.java:[9,11] error: Cls is not abstract and does not override abstract method get(List) in Interface TypeTest.java:[12,20] error: name clash: get(List<List<Object>>) in Cls and get(List<List<Object>>) in Interface have the same erasure, yet neither overrides the other
But this works:
public interface Interface<T> {
List<T> get();
}
public class Cls implements Interface {
@Override
public List get() {
return Collections.emptyList();
}
}
as does this:
public interface Interface<T> {
List<T> get(List<List<Object>> keys);
}
public class Cls implements Interface {
@Override
public List get(List keys) {
return Collections.emptyList();
}
}
I don't understand this behaviour. The type erasure only applies to the type paramter T, does it not? Why does missing out T affect the unrelated List<List<Object>> keys parameter?
You are getting a compiler error in your first example because of the type erasure that occurs with raw types. Section 4.8 of the JLS describes the circumstances under which using a raw type would yield type erasure:
To facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type (§10.1) whose element type is a parameterized type. Such a type is called a raw type.
and
The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of its parameterized invocations.
Other clauses in this section also describe how the types of fields, return types of methods, and parameter types of methods are also subject to type erasure.
Moving on to Section 4.6 of the JLS, as referred above:
The type parameters of a constructor or method (§8.4.4), and the return type (§8.4.5) of a method, also undergo erasure if the constructor or method's signature is erased.
Therefore, the erasure of the method get is
List get(List);
Your initial example will not compile because the method signature for Cls's get method, get(List<List<Object>>) does not match the raw interface's method, and it doesn't implement the raw interface's get method.
That is also why your second and third examples compile -- they properly override the raw interface Interface.
Incidentally, to override the generic Interface interface without resorting to implementing the raw type, supply a type argument in the implements clause:
public interface Interface<T> {
List<T> get(List<List<Object>> keys);
}
Implementing it:
class Cls<T> implements Interface<T> {
@Override
public List<T> get(List<List<Object>> keys) {
return Collections.emptyList();
}
}
Or
class Cls implements Interface<String> {
@Override
public List<String> get(List<List<Object>> keys) {
return Collections.emptyList();
}
}
Any reference type would work in implements Interface<String>, as long as the type matches the return type in get. The clause implements Interface<Integer> would work with the method List<Integer> get(List<List<Object>> keys).
In short, type erasure occurs for all parameterized types and generic types in a raw class/interface, regardless of whether they are related to the generic type parameters declared on class/interface.
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