问题
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