Given the following setup:
public class TestType {
public static void main(String[] args) {
List<Constants> list = new ArrayList<>();
accept(list); //Does not compile
}
static void accept(Iterable<MyInterface> values) {
for (MyInterface value : values) {
value.doStuff();
}
}
}
interface MyInterface<T> {
T doStuff();
}
enum Constants implements MyInterface<Integer> {
ONE, TWO, THREE;
@Override
public Integer doStuff() {
return ordinal();
}
}
Why won't the compiler accept the list as parameter to accept()?
List
extends Iterable
via Collection
so that isn't the problem.
On the other hand, the compiler tells me that
incompatible types: java.util.List<enums.Constants> cannot be converted to java.lang.Iterable<enums.MyInterface>
But Constants IS a MyInterface... isn't it?
Java enums will be enhanced with generics support and with the ability to add methods to individual items, a new JEP shows. Since both features can be delivered with the same code change, they are bundled together in the same JEP. The change only affects the Java compiler, and therefore no runtime changes are needed.
Generics make a class, interface and, method, consider all (reference) types that are given dynamically as parameters. This ensures type safety. Generic class parameters are specified in angle brackets “<>” after the class name as of the instance variable. Generic constructors are the same as generic methods.
What is subtyping? Subtyping is a key feature of object-oriented programming languages such as Java. In Java, Sis a subtype of T if S extends or implements T. Subtyping is transitive, meaning that if R is a subtype of S, then R is also a subtype of T (T is the super type of both Sand R).
By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.
The problem is with how Generics work. Specifically, Generics are non-reified... meaning that the compiler will not see an Iterable<enum.Constants>
as an Iterable<enum.MyInterface>
even if Constants is a sub-class of MyInterface.
However, there is a way to get around it: Generic wildcards.
If you change static void accept(Iterable<MyInterface> values)
to static void accept(Iterable<? extends MyInterface> values)
, it should work.
You need to use Iterable<? extends MyInterface>
instead of Iterable<MyInterface>
because even though Constants
is a subtype of MyInterface
, Iterable<Constants>
is not a subtype of Iterable<MyInterface>
- and I'll show you why:
If it was so (let's use List
instead of Iterable
for the next example), I would be able to do this:
List<Constant> constantsList = new ArrayList<Constants>(); // list of constants
List<MyInterface> ifaceList = constantsList; // you said this would be OK ...
// assume MyOtherImplementation is another implmentation of MyInterface
ifaceList.add(new MyOtherImplementation()); // OK, MyOtherImplementation implements MyInterface
Constant myConst = constantsList.get(0); // Oops! I just got an instance of MyOtherImplementation from List<Constant> - not cool.
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