9

ジェネリック型パラメーターを持つインターフェイスを使用しているとします

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ます。この場合、 to の型パラメータを追加する必要があるよう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);
  }
}

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
  ... 
}
4

7 に答える 7

6

役に立つように、ある時点で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();
    }
}
于 2009-03-30T23:43:46.557 に答える
4

あなたの問題は「キャプチャヘルパー」によって解決されたものと似ていますが、2つの別々の方法が使用されている2番目の例に適用できるかどうかはわかりません. 最初のメソッドは、型パラメーターに関係なく機能するため、doStuff間違いなくより適切に記述できます。次に、「キャプチャ ヘルパー」パターンが役立ちます。public void doStuff(Foo<?> foo)Foo


更新: 少しいじって、Goetz のキャプチャ ヘルパーのアイデアを拡張した後、これを思いつきました。内部は少し乱雑に見えます。外からは、何も疑わないでしょう。

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);
    }
  }
}
于 2009-03-30T23:19:24.993 に答える
1

3 層の階層を持たない理由:

abstract class Foo

abstract class FooImplBase<T> extends Foo

class Bar extends FooImplBase<String>

Fooクライアントは、一般的なメソッドを含まないについてのみ知っています。必要な汎用メソッドを導入してFooImplBase<T>から、具体的なクラスを派生させます。

したがって、あなたの例startStuff()endStuff()は、 で抽象化されFoo、 で実装されFooImplBase<T>ます。それはあなたの実際の状況でうまくいくように聞こえますか? ちょっと面倒なのは同意。

于 2009-03-30T23:15:36.390 に答える
1

Bar クラスを定義しています。2つのことは真実です...

1) Bar に関連するパラメトリック型はありません。つまり、foo および t メンバーには、定義用に固定された単一の型 (U など) があります。foo に割り当てることが期待される Foo を渡された場合、それは Foo<U> でなければなりません。これがすべて当てはまる場合、はい、それはパブリック インターフェイスの一部ではありません。すべてが特定の型を持っています。それから、自由な型の変数がないので、「数量化」の意味がわかりません。普遍的に数量化することを意味する場合、Bar にはパラメトリック型付けがないため、そのメンバーのそれぞれに具体的な型が与えられているに違いないという事実をどのように調整しますか?

2) Bar に関連するパラメトリック型があります。明らかに public インターフェイスにあるとは限りませんが、おそらく Foo<T> を渡すので、Bar クラスを複数の型でインスタンス化する必要があります。次に、前述のように、これはパブリック インターフェイスにあり、ジェネリックを使用して T で Bar をパラメトリックにする必要があります。これにより、「すべての型 T について、この定義は真である」という定義の普遍的な数量化の形式が得られます。

于 2009-03-30T23:16:55.670 に答える
0

Bar クラス内の 'T' に使用する型をどこかで決定する必要があります。したがって、Bar の定義でそれを選択する (クラス定義内で Foo を Foo に置き換える) か、Bar クラスのクライアントに任せる必要があります。この場合、Bar はジェネリックにする必要があります。

Tに依存しない Bar へのインターフェースを持ち、Tに異なる型を選択できるようにする場合は、次のように、非ジェネリック インターフェースまたは抽象基本クラスを使用する必要があります。

interface Bar {
  void startStuff();
  // ...
}

class BarImplement<T> {
  private Foo<T> foo;
  // ...
}
于 2009-03-30T23:55:46.170 に答える
-2

この場合のパラメーター T は、コンパイル時に Object に消去されるため、Bar に関する限り役に立ちません。したがって、「手間を省いて」、消去を早めに行うこともできます。

public class Bar {

    private Foo<Object> foo;
    private Object t;

  ... 
}
于 2009-03-30T23:18:58.193 に答える