4

我正在开发一个流畅的 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 成为抽象类而不是接口,以防问题与此有关,但这没有帮助。

4

1 回答 1

6

在这里,您的接口A声明T了扩展原始类型的类型参数A。你应该试试

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

这可以确保T扩展泛型Awith T。这样,T将在Query接口中指定的范围内。

这将适用于您的AImpl该工具的第二个版本A<AImpl>

编辑

不得不说

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

看起来过于复杂。对于B接口,扩展它A就像你扩展Afrom一样Query

//                                    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() { ... }
}
于 2013-05-14T21:43:39.863 回答