I work on a free API and try to use common Java methods, offering an elegant API that handles type conversions for my users. I am having problems with the fact that it works, although due to type erasure.
Here is a simplified version of my interfaces that shows the problem I am facing:
interface Query<T extends Query<T>> { T execute(); Query<T> appendClause(); } interface A<T extends A> extends Query<T> { } class AImpl implements A<A> { A execute() { ... } Query<A> appendClause() { ... } }
I get the error AImpl.appendClause() . The compiler says that A not within its bounds and should extend Query. As far as I can tell, my declaration that AImpl implements A<A> means that A extends Query<A> .
Following another answer here, I tried breaking up any potential unsolvable recursion by changing AImpl to:
class AImpl implements A<AImpl> { AImpl execute() { ... } Query<AImpl> appendClause() { ... } }
Now I get a compilation of errors A , which says that "a parameter of type T is not within its boundary."
Does anyone have any suggestions on how to handle this? Java generics give me a headache.
EDIT
I changed the definition of A to
interface A<T extends A<T>> extends Query<T> { }
And it got a second implementation of AImpl. But I also want to extend the request request API for subclasses of A:
interface B<T extends B> extends A<B> { } class BImpl implements B<BImpl> { BImpl execute() { ... } Query<BImpl> appendClause() { ... } }
This definition causes an error in declaration B : "The type of parameter B is not within its boundary, must extend A".
I can fix this error by changing B to
interface B<T extends B<T>> extends A<B<T>> { }
but now my interface definitions are starting to look ridiculous, and I feel like I'm doing something wrong. Also, I still get the error in BImpl: "appendClause () in BImpl cannot implement appendClause () in Query, trying to use an incompatible return type."
Any suggestions on how I can clear my subclass definitions, so I don’t need to specify the whole inheritance hierarchy in extends or how can I get BImpl to work?
EDIT 2
Ok, I ran into another problem. I have a factory class that generates queries:
public class QueryFactory { public static <T extends Query<T>> Query<T> queryForType(Class<T> type) { ... } }
and client code:
Query<B> bQuery = QueryFactory.queryForType(B.class);
My client code gives me an error in the declaration for bQuery: “The parameter type“ B ”is not within its border, should extend“ Query. ”At that moment, I thought that B really extends Query ...
This error disappears if I change the queryForType () call to
Query<? extends B> bQuery = QueryFactory.queryForType(B.class);
but I still get a warning from the compiler:
unchecked method invocation: <T>queryForType(Class<T>) in QueryFactory is applied to Class<B> unchecked conversion found: Query required: Query<B>
It seems like erasing styles is struggling with me again, but I don't understand these warnings. Any other suggestions to get me back on track? Thanks!
EDIT 3
I can compile it without warning if I change the client code to
Query<BImpl> bQuery = QueryFactory.queryForType(BImpl.class);
But I would really like to hide implementation classes from API users. I tried to make B an abstract class instead of an interface in case the problem is related to this, but that did not help.