私は流暢な 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
が範囲内にないため、クエリを拡張する必要があると言います。私が知る限り、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 の 2 番目の実装が機能しました。しかし、A のサブクラスを照会できるように API を拡張したいとも考えています。
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 でまだエラーが発生します。
サブクラス定義をクリーンアップして、継承階層全体を指定する必要がないようにする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はクエリを拡張したと思いました...
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 をインターフェイスではなく抽象クラスにしようとしましたが、役に立ちませんでした。