简体   繁体   中英

Java Generics with inheritance and covariance

I'm having a bit of a hard time to understand how to correctly use covariance and wildcards when using generics in Java.

I'm trying to avoid having to use casting.

Here's my situation :

class Element {}

class ChildElement extends Element {}

interface A<T> {
    T doSomething(T foo);
}

interface B extends A<Element> {}

interface C extends B {}

class D implements B {

    @Override
    public Element doSomething(Element foo) {} 
}

class E extends D implements C {
    @Override
    public Element doSomething(Element foo) {}
}

This situation works, but I would like to be able to do this instead for class E :

class E extends D implements C {

    @Override
    public ChildElement doSomething(ChildEment foo) {}
}

From what I've seen, what I want to do is covariance, but I can't do it in the current sitution as I need to use wildcards. But I've read that you can't do covariance with generics and wildcards.

Is there any solution to this problem ? I'd like to keep the strong dependencies between each classes.

Thanks for your help !

You have to make B generic, but you can put a bound on the generic parameter such that it's at least an Element :

interface B<T extends Element> extends A<T> {}

interface C<T extends Element> extends B<T> {}

Then about the closest you can get is:

 class D<T extends Element> implements B<T> {
    @Override
    public T doSomething(T foo) { return null;}
}

class E extends D<ChildElement> implements C<ChildElement> {
    @Override
    public ChildElement doSomething(ChildElement foo) { return null;}
}

which compiles.

But I've read that you can't do covariance with generics and wildcards.

This statement is not about covariant return type, but it means that you can't do this:

List<Number> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
l1 = l2; //illegal

But T it's also any subclass of T. So thing like this:

interface Interface<T> {
    T method();
}

class SomeClass implements Interface<Number>{
    @Override
    public Float method() {
        return 1F;
    }
}

works fine. But method's signature of subclass(implementation) and superclass(interface) must matches. So in your example

class E extends D implements C {

    @Override
    public ChildElement doSomething(ChildEment foo) {}
}

it's illegal.

The interface you've described can't be covariant. What would you want/expect to happen with this code?

class OtherElement extends Element{}
Element oe = new OtherElement();
E e = new E();
B b = e;
b.doSomething(oe);

This compiles correctly, as it must - but if E#doSomething expected a ChildElement we would have a failure here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM