How can I build an interface hierarchy for my free API?

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.

+4
source share
1 answer

Here, your interface A declares a parameter of type T , which extends the original type of A You must try

 // v--- Add this interface A<T extends A<T>> extends Query<T> { } 

This ensures that T extends generalized A with T Thus, T will be within its boundary specified in the Query interface.

This will work with the second version of AImpl , which implements A<AImpl> .

EDIT

Having say

 interface B<T extends B<T>> extends A<B<T>> { } 

It looks too complicated. For interface B extend it from A just as you extended A from Query :

 // v-- Don't mention B here interface B<T extends B<T>> extends A<T> { } 

Then your BImpl class may look just like your AImpl class:

 class BImpl implements B<BImpl> { public BImpl execute() { ... } public Query<BImpl> appendClause() { ... } } 
+6
source

Source: https://habr.com/ru/post/1480810/


All Articles