我正在开发一个流畅的 API,并试图利用 Java 的通用方法来提供一个优雅的 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
声明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> { }
这使得 AImpl 的第二个实现工作。但我还想扩展 API,使其能够查询 A 的子类:
interface B<T extends B> extends A<B> { }
class BImpl implements B<BImpl> {
BImpl execute() { ... }
Query<BImpl> appendClause() { ... }
}
这个定义让我在B
的声明中出错:“类型参数 B 不在其范围内;应该扩展 A”。
我可以通过将 B 更改为来清除该错误
interface B<T extends B<T>> extends A<B<T>> { }
但是现在我的接口定义开始看起来很荒谬,我觉得我做错了什么。另外,我仍然在 BImpl 中遇到错误:“BImpl 中的 appendClause() 无法在 Query 中实现 appendClause();尝试使用不兼容的返回类型”。
关于如何清理子类定义的任何建议,因此我不需要在中指定整个继承层次结构extends
或如何让 BImpl 工作?
编辑 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' 不在其范围内;应该扩展 'Query'”。在这一点上,我认为 B 确实扩展了 Query ......
如果我将 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 成为抽象类而不是接口,以防问题与此有关,但这没有帮助。