2013-05-14 27 views
4

我正在研究一个流畅的API,并试图利用Java的泛型方法来提供一个优雅的API来处理我的用户的类型转换。由于类型擦除,我遇到了一些麻烦让它工作。如何为我的流利API构建一个接口层次结构?

这里是我的接口的简化版本,显示我运行到这个问题:

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() { ... } 
} 

我对AImpl.appendClause()得到一个错误。编译器说A不在其范围内,应该扩展Query。据我所知,我的声明AImpl implements A<A>意味着A确实延伸Query<A>

继另一个答案在这里,我试图通过改变AImpl到分手任何可能无法解决的递归:

class AImpl implements A<AImpl> { 
    AImpl execute() { ... } 
    Query<AImpl> appendClause() { ... } 
} 

现在,我得到一个错误,编译A,上面写着“类型参数T是不在其绑定” 。

有没有人对如何处理这个问题有任何建议? Java的泛型让我头疼。

编辑

我已经改变了A的定义

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

而这得到了第二次执行的AIMP1的工作。但我也想用用来查询的子类的能力扩展API:

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

class BImpl implements B<BImpl> { 
    BImpl execute() { ... } 
    Query<BImpl> appendClause() { ... } 
} 

这个定义让我的错误B的声明:“类型参数B不是内的束缚,应延长”。

我可以b变更清理是错误

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

但现在我的接口定义也开始看起来很荒谬,我得到我做错了什么的感觉。另外,我仍然在BImpl中收到错误:“BImpl中的appendClause()无法在Query中实现appendClause();尝试使用不兼容的返回类型”。

关于如何清理我的子类定义的任何建议,所以我不需要指定extends中的整个继承层次结构或我如何才能使BImpl工作?

EDIT 2

好吧,我遇到了另一个问题。我有一个生成的查询工厂类:

public class QueryFactory { 
    public static <T extends Query<T>> Query<T> queryForType(Class<T> type) { ... } 
} 

和客户端代码:

Query<B> bQuery = QueryFactory.queryForType(B.class); 

我的客户端代码是给我的声明bQuery的错误:“类型参数‘B’不在范围它的约束;应该扩展'查询'“。我想在这一点上B均无扩展查询...

这个错误消失,如果我改变queryForType()调用

Query<? extends B> bQuery = QueryFactory.queryForType(B.class); 

,但我仍然得到来自编译器的警告未检查:

unchecked method invocation: <T>queryForType(Class<T>) in QueryFactory is applied to Class<B> 
unchecked conversion found: Query required: Query<B> 

看起来类型擦除再次打我,但我不明白这些警告。还有什么建议让我回到正轨?谢谢!

编辑3

我可以得到它编译没有警告,如果我的客户端代码更改为

Query<BImpl> bQuery = QueryFactory.queryForType(BImpl.class); 

但我真的很想从API的用户隐藏实现类。我试图让B成为一个抽象类,而不是一个接口,以防问题与此有关,但它没有帮助。

+0

应该采取什么类型查询>代表?我认为签名没有意义。例如我希望一个'查询'返回'Apple'的实例,而不是'查询''为执行()'做的一个实例? – Pyranja

+0

苹果扩展查询。在这种情况下,execute()会返回一个Apple,并且您可以在Apple上调用appendClause()来查找更具体的Apple。 – Greg

+0

给你阅读这篇文章http://www.unquietcode.com/blog/2011/programming/using-generics-to-build-fluent-apis-in-java/ – cmbaxter

回答

6

在这里,你的界面A声明键入扩展原料A参数T。你应该尝试

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

这可以确保T扩展了泛型化AT。这样一来,T会内的绑定在Query接口中指定。

这将与您的AImpl秒版本实现A<AImpl>工作。

编辑

不必说

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

看起来过于复杂。对于B接口,从A就像你扩展AQuery扩展它:

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

然后你BImpl类可以类似于您AImpl类:

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

好的,那就摆脱了A上的错误。我仍然遇到试图将子类A的问题。编辑我的问题以反映新问题。 – Greg

+0

我已经修改了我的答案,以回应您修正的问题。 – rgettman

+0

我试图添加一个工厂类来生成查询时遇到了另一个问题。我解决了它,并在另一个答案中记录了该解决方案。 – Greg