I have two abstract generic classes. They cooperate and hence depend on each other. Occasionally one needs to pass this
to the other. I am trying to find a type safe way to do this:
public abstract class AbstractA<T extends AbstractB<? extends AbstractA<T>>> {
protected void foo() {
T aB = createB();
aB.setA(this);
}
/** factory method */
abstract public T createB();
}
public abstract class AbstractB<T extends AbstractA<? extends AbstractB<T>>> {
private T theA;
@SuppressWarnings("unchecked")
public void setA(AbstractA<? extends AbstractB<?>> theA) { // dreamed of parameter list (T theA)
// Unchecked cast from AbstractA<capture#1-of ? extends AbstractB<?>> to T
this.theA = (T) theA;
}
protected T getA() {
return theA;
}
}
My question is whether I can find a cleaner way so I avoid the unchecked cast in AbstractB.setA()
. I had hoped to declare it setA(T theA)
, but then the call to it won’t compile: The method setA(capture#1-of ? extends AbstractA<T>) in the type AbstractB<capture#1-of ? extends AbstractA<T>> is not applicable for the arguments (AbstractA<T>). I am still struggling to understand whether the compiler should know enough to allow it or not.
I was thinking my problem may be related to the one discussed in Java generics compilation error – The method method(Class<capture#1-of ? extends Interface>) in the type <type> is not applicable for the arguments. My unchecked cast was inspired from there. I liked the reply by Tom Hawtin – tackling, but I have not found a way to apply it to my situation.
My user will declare concrete subclasses and instantiate one ConcreteA
and any number of ConcreteB
s:
public class ConcreteA extends AbstractA<ConcreteB> {
@Override
public ConcreteB createB() {
return new ConcreteB();
}
public void concreteAMethod() {
// ...
}
}
public class ConcreteB extends AbstractB<ConcreteA> {
public void bar() {
ConcreteA a = getA();
a.concreteAMethod();
}
}
(class AbstractA<T extends AbstractB<? extends AbstractA<T>>>
looks a bit complicated; I thought I needed it for concrete subclasses to know each other’s exact types, but apparently it doesn’t give me that.)
Advertisement
Answer
I have come to realize that my problem really came out of stuffing two concepts into the AbstractA/ConcreteA hierarchy that didn’t belong together. Though maybe not interesting to very many, I am posting this insight for two reasons: (1) I feel I owe Chris Wohlert the answer I have found myself (2) more importantly, I’d love to inspire anyone else facing a similar tricky generics issue to review your design from a higher level than just solving the generics and/or class cast issue. It certainly helped me. The cast/generics problem was a sign that something more fundamental was not quite right.
public abstract class AbstractA {
public void foo() {
AbstractB aB = createB();
aB.setA(this);
}
/** factory method */
abstract public AbstractB createB();
}
public abstract class AbstractB {
private AbstractA theA;
public void setA(AbstractA theA) {
this.theA = theA;
}
// methods that use theA
}
No generics and no class cast. Taking out the stuff that didn’t belong in the A class hierarchy into ConcreteC (with no AbstractC):
public class Client {
public void putTheActTogether() {
ConcreteC theC = new ConcreteC();
// the concrete A
AbstractA theA = new AbstractA() {
@Override
public AbstractB createB() {
return new ConcreteB(theC);
}
};
// call methods in theA
}
}
public class ConcreteB extends AbstractB {
private final ConcreteC c;
public ConcreteB(ConcreteC c) {
super();
this.c = c;
}
public void bar() {
c.concreteCMethod();
}
}
public class ConcreteC {
public void concreteCMethod() { // was concreteAMethod(); moved and renamed
// ...
}
}
The client needs a few more lines than before. In my real-world code I needed to duplicate one final field in AbstractA and ConcreteC, but it made sense to do. All in all I consider it a low price for a design that is otherwise pure and simple.