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.