Java: using generic wildcards with subclassing

…衆ロ難τιáo~ 提交于 2019-12-13 07:03:51

问题


Say I have a class Foo, a class A and some subclass B of A. Foo accepts A and its sublclasses as the generic type. A and B both require a Foo instance in their constructor. I want A's Foo to be of type A , and B's Foo to be of type B or a superclass of B. So in effect, So I only want this:

Foo<X> bar = new Foo<X>;
new B(bar);

to be possible if X is either A, B, or a both subclass of A and superclass of B.

So far this is what I have:

class Foo<? extends A>{
    //construct
}


class A(Foo<A> bar){
    //construct
}

class B(Foo<? super B> bar){
    super(bar);
    //construct
}

The call to super(...) doesn't work, because <A> is stricter than <? super B>. Is it somehow possible to use the constructor (or avoid code duplication by another means) while enforcing these types?

Edit: Foo keeps a collection of elements of the generic parameter type, and these elements and Foo have a bidirectional link. It should therefore not be possible to link an A to a Foo.


回答1:


If you change the A constructor to:

class A(Foo<? extends A> bar){
    //construct
}

will it do what you want ?

If you really want to limit the constructor of A to Foo then you need to provide another protected method (aka usable from derived classes) to set the Foo instance. Something like this:

public class A {

    Foo<?> foo;

    public A(Foo<A> foo) {
        setFoo(foo);
    }

    protected A() {
    }

    protected void setFoo(Foo<?> foo) {
        this.foo = foo;
    }
}

and B

public class B extends A {

    public B(Foo<? super B> foo) {
        setFoo(foo);
    }
}

now this works:

new A(new Foo<A>());
new A(new Foo<B>()); // this fails compilation
new B(new Foo<B>());

In order for foo element in A to be properly typed you might need to make A a parametrized class too.




回答2:


The only way to do this would be to have...

class A(Foo<? extends A> bar) {
  //construct
}

But it appears this isn't what you want. You can't have the other approach because when you create an instance of B you are also creating an instance of A (that is part of the B instance). So B can't take in special fields for the parts in A. I'm not sure why you wouldn't allow A's foo to be of type B, could you perhaps expand on that?




回答3:


The following setup compiled for me:

public interface MarkerInterface {

}

public class A implements MarkerInterface {

    public A(Foo<A> fooA) {

    }
}

public class SuperB implements MarkerInterface {

}


public class B extends SuperB {

    public B(Foo<? super B> fooB) {

    }
}

And with the main method:

public static void main(String[] args) {
    B b = new B(new Foo<SuperB>());
}

Is this what you're looking for?




回答4:


Java generics are powerful and well designed; nevertheless we sometimes find them lacking. I don't think there's a good way to do what you want.

The easiest thing to do is to change the declaration of Foo<X> to Foo<X extends A>. If that's not acceptable, you can subclass Foo like this:

class Foo<X> { }

class FooA<X extends A> extends Foo<X> { }

class A {
    public A(FooA<? extends A> foo) { }
}

class B extends A {
    public B(FooA<? super B> foo) {
        super(foo);
    }
}

(Note: if you have trouble following, when you see Foo, think ArrayList.)

This has the obvious disadvantage that you have to use FooA rather than Foo, hindering code reuse.



来源:https://stackoverflow.com/questions/4626317/java-using-generic-wildcards-with-subclassing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!