質問
ジェネリック型パラメーターを持つインターフェイスを使用しているとします。
interface Foo<T> {
T getOne();
void useOne(T t);
}
意図はそのタイプです T
抽象的です:の実装に型制約を強制します。 Foo
, 、しかしクライアントコードは正確に何を気にするわけではありません T
は。
これは、ジェネリック メソッドのコンテキストでは問題ありません。
public <T> void doStuff(Foo<T> foo) {
T t = foo.getOne();
/* do stuff */
foo.useOne(t);
}
しかし、私が仕事を中断したいとします。 doStuff
, 、クラス内の一部の状態を保存します Bar
. 。この場合、次の type パラメータを追加する必要があるようです。 Foo
に Bar
.
public class Bar<T> {
private Foo<T> foo;
private T t;
/* ... */
public void startStuff() {
t = foo.getOne();
}
public void finishStuff() {
foo.useOne(t);
}
}
type パラメータなので、これはちょっと奇妙です。 T
の公開インターフェイスには表示されません Bar
(つまり、メソッドのパラメータや戻り値の型には含まれません)。「数値化する」方法はあるのでしょうか? T
離れて"?つまり、パラメータを手配できますか T
のインターフェースに隠蔽される Bar
, 、次のようになりますか?
public class Bar {
<T> { // foo and t have to use the same T
private Foo<T> foo;
private T t;
} // T is out of scope
...
}
解決
あなたの問題は、「捕捉することによって解決と同様であり、ヘルパー」、のが、私は2つの別々の方法が使用されている場合、それはあなたの第二の例に適用することができますかわかりません。それは関係なく、doStuff
型パラメータの働くので、あなたの最初のpublic void doStuff(Foo<?> foo)
方法は間違いなく良い、Foo
のように書くことができます。次に、「キャプチャヘルパー」パターンが有用であろう。
更新:ビットをいじった後、ゲッツのキャプチャヘルパーのアイデアを拡張し、私はこれを思い付きました。内部に、それは少し厄介に見えます。外から、あなたは事を疑わないでしょう。
public class Bar {
private final Helper<?> helper;
public Bar(Foo<?> foo) {
this.helper = Helper.create(foo);
}
public void startStuff() {
helper.startStuff();
}
public void finishStuff() {
helper.finishStuff();
}
private static class Helper<T> {
private final Foo<T> foo;
private T t;
private Helper(Foo<T> foo) {
this.foo = foo;
}
static <T> Helper<T> create(Foo<T> foo) {
return new Helper<T>(foo);
}
void startStuff() {
t = foo.getOne();
}
void finishStuff() {
foo.useOne(t);
}
}
}
他のヒント
有用であるためには、いくつかの点で、あなたはfoo
フィールドを設定しようとしています。その時点で、あなたが知っている(またはキャプチャすることができる)T
をすべきです。私は、コンストラクタでそれを行うことをお勧めし、Bar
は、一般的なパラメータを持っているため、それは意味をなさないと思います。クライアントコードは、タイプを参照してくださいする必要がありませんので、あなたも、インタフェースを使用することができます。しかし、私はあなたが私のアドバイスを取り、本当にsetFoo
をするつもりされていないと仮定します。だから切り替え可能な実装にポイントを追加します:
/* pp */ class final BarImpl<T> {
private final Foo<T> foo;
private T t;
BarImpl(Foo<T> foo) {
this.foo = foo;
}
public void startStuff() {
t = foo.getOne();
}
public void finishStuff() {
foo.useOne(t);
}
}
public final class Bar {
private BarImpl<?> impl;
/* ... */
// Need to capture this wildcard, because constructors suck (pre-JDK7?).
public void setFoo(Foo<?> foo) {
setFooImpl(foo);
}
private <T> void setFooImpl(Foo<T> foo) {
impl = new BarImpl<T>(foo);
}
public void startStuff() {
impl.startStuff();
}
public void finishStuff() {
impl.finishStuff();
}
}
なぜ、3層の階層構造を持っています:
abstract class Foo
abstract class FooImplBase<T> extends Foo
class Bar extends FooImplBase<String>
クライアントは、唯一の任意の一般的なメソッドが含まれていないFoo
、知っています。あなたがFooImplBase<T>
に必要なすべての一般的な方法を紹介し、次に具体的なクラスはそれから派生します。
だからあなたの例ではstartStuff()
とendStuff()
はFoo
に抽象的だろうとFooImplBase<T>
に実装。それのようなその音は、あなたの実際の状況に働くかもしれないのか?私はそれが少し面倒です同意します。
Bar クラスを定義しています。二つのことが真実です...
1) Bar に関係するパラメトリック タイプはありません。つまり、foo メンバーと t メンバーは、定義に対して固定された単一の型 (たとえば U) を持ちます。foo に割り当てる Foo が渡された場合、それは Foo<U> である必要があります。これがすべて真実である場合、はい、それはパブリック インターフェイスの一部ではありません。すべてが特定のタイプを持っています。次に、自由型の変数がないため、「定量化」が何を意味するのかわかりません。普遍的に定量化するという意味である場合、Bar にはパラメトリック型付けがないため、そのメンバーごとに具体的な型が与えられなければならないという事実をどのように調整すればよいでしょうか?
2) Bar にはパラメトリック型が関係しています。パブリック インターフェイスには明らかに存在しないかもしれませんが、おそらく Foo<T> を渡すため、複数の型で Bar クラスをインスタンス化したいと考えられます。次に、前述したように、これはパブリック インターフェイスにあり、ジェネリックを使用して T で Bar をパラメトリックにする必要があります。これにより、「すべての型 T に対して、この定義は真である」という定義に対する何らかの形式の普遍的な定量化が得られます。
どこかで、あなたがバークラス内の「T」のために使用したいどのようなタイプ、決定されなければなりません。あなたは(クラス定義の内部ではFooではFooを置き換える)バーの定義にそれを選んだ必要があります。また、バークラスのクライアントに任せるので、どちらか:この場合、バーは、一般的な作られなければならない。
。 あなたはT に頼るとはTのためのさまざまなタイプを選んだことはできませんバーするためのインタフェースを持つようにしたい場合は、、あなたは同様に、非ジェネリックインターフェイスまたは抽象基本クラスを使用する必要があります。
interface Bar {
void startStuff();
// ...
}
class BarImplement<T> {
private Foo<T> foo;
// ...
}
、あなたはFooのインターフェイス内のバーのクラスを入れて、その道のTを吸うことができます。インタフェース内部クラスの詳細についてはこちらの記事のnoreferrer">
コンパイル時にObjectに消去されますので、この場合は、パラメータTは、限りバーに関しては無用になります。だから、あなたにも「自分に手間を省く」、および早期の消去を行うことができます:public class Bar {
private Foo<Object> foo;
private Object t;
...
}