13

異なるタイプの T と S の 2 つの List を取り、両方の要素を含む 1 つの List を返す "mix" メソッドがあるとします。型の安全性のために、返される List が R 型であることを指定したいと思います。ここで、R は T と S の両方に共通のスーパータイプです。たとえば、次のようになります。

List<Number> foo = mix(
    Arrays.asList<Integer>(1, 2, 3),
    Arrays.asList<Double>(1.0, 2.0, 3.0)
);

これを指定するには、メソッドを次のように宣言します。

static <R, T extends R, S extends R> List<R> mix(List<T> ts, List<S> ss)

mixしかし、クラスで static ではなくインスタンス メソッドを作成したい場合はどうすればよいList2<T>でしょうか。

<R, T extends R, S extends R> List<R> mix ...

<T>のインスタンスで をシャドウするList2ので、それは良くありません。

<R, T extends S&T, S extends R> List<R> mix ...

シャドーイングの問題を解決しますが、コンパイラには受け入れられません

<R super T, S extends R> List<R> mix ...

下限のワイルドカードは名前付き変数に格納できないため、コンパイラによって拒否されます (? super X式でのみ使用されます) 。

のように、引数をクラス自体に移動することもできますList2<R, T extends R, S extends R>が、型情報は実際にはインスタンス レベルにあるわけではありません。これは、1 つのメソッド呼び出しにしか使用されず、必要なたびにオブジェクトを再キャストする必要があるためです。異なる引数でメソッドを呼び出します。

私が知る限り、ジェネリックでこれを行う方法はありません。私ができる最善List2の方法は、ジェネリックが導入される前のように、生を返し、呼び出しサイトでキャストすることです。誰かがより良い解決策を持っていますか?

4

2 に答える 2

8

質問とコメントで述べたように、次の署名が理想的です。

<R super T, S extends R> List<R> mix(List<S> otherList)

ただし、もちろん、R super T 言語では許可されていません(リンクされた投稿での polygenelubricants の回答が間違っていることに注意してください。質問が示すように、この構文には使用例があります)。

ここで勝つ方法はありません - 選択できるいくつかの回避策の 1 つしかありません。

  • 生の型の署名を使用することに頼ってください。これをしないでください。
  • mix静的メソッドを保持します。これは、ポリモーフィズム関連の理由でクラスのインターフェイスの一部にする必要がある場合や、mix静的なままにしておくことが受け入れられないと思われる一般的に使用されるメソッドにする予定がない限り、実際には適切なオプションです。
  • 過度に制限的であるという署名で解決し、mix特定のチェックされていないキャストが呼び出し元の側で必要になることを文書化します。これはGuavaOptional.orしなければならなかったことと似ています。そのメソッドのドキュメントから:

ジェネリックに関する注意: 署名public T or(T defaultValue)は過度に制限的です。ただし、理想的な署名 はpublic <S super T> S or(S)、正当な Java ではありません。その結果、サブタイプを含むいくつかの賢明な操作はコンパイル エラーになります。

Optional<Integer> optionalInt = getSomeOptionalInt();
Number value = optionalInt.or(0.5); // error

Optional<? extends T>回避策として、 toをキャストすることは常に安全Optional<T>です。[上記のOptionalインスタンス] をOptional<Number>(Numberは目的の出力タイプ) にキャストすると、問題が解決します。

Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
Number value = optionalInt.or(0.5); // fine

残念ながら、にキャストするのは必ずしも安全ではありません。たとえば、aを aにキャストすると、sのみを保持するはずのものに a が追加され、予期しない実行時エラーが発生する可能性があります。例外は( のように) 不変の場合ですが、これはありそうにありません。List2<? extends T>List2<T>List2<Integer>List2<Number>DoubleIntegerList2Optional

それでも、注意してタイプセーフでないコードを説明付きで文書化していれば、そのようなキャストを回避できます。mix次の署名(および楽しみのための実装)があると仮定します。

List<T> mix(final List<? extends T> otherList) {

    final int totalElements = (size() + otherList.size());
    final List<T> result = new ArrayList<>(totalElements);

    Iterator<? extends T> itr1 = iterator();
    Iterator<? extends T> itr2 = otherList.iterator();
    while (result.size() < totalElements) {
        final T next = (itr1.hasNext() ? itr1 : itr2).next();
        result.add(next);
        final Iterator<? extends T> temp = itr1;
        itr1 = itr2;
        itr2 = temp;
    }

    return result;
}

次に、次の呼び出しサイトがある場合があります。

final List2<Integer> ints = new List2<>(Arrays.asList(1, 2, 3));
final List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);

final List<Number> mixed;
// type-unsafe code within this scope
{
    @SuppressWarnings("unchecked") // okay because intsAsNumbers isn't written to
    final List2<Number> intsAsNumbers = (List2<Number>)(List2<?>)ints;
    mixed = intsAsNumbers.mix(doubles);
}

System.out.println(mixed); // [1, 1.5, 2, 2.5, 3, 3.5]

繰り返しになりますが、static の解決mixはよりクリーンになり、型安全性へのリスクはありません。そのようにしない十分な理由があることを確認します。

于 2013-08-20T03:55:13.090 に答える
0

あなたの質問でわからないのは、これらのサブクラスが拡張するスーパータイプをすでに知っているか、または特定のスーパークラスの 2 つのサブタイプを渡す完全にジェネリックなメソッドが必要かということです。

最初のケースでは、抽象クラスといくつかのサブタイプを使用して、最近同様のことを行いました。

public <V extends Superclass> List<Superclass> mix(List<V> list1, List<V> list2) {
  List<Superclass> mixedList;

  mixedList.addAll(list1);
  mixedList.addAll(list2);
}

後者の場合は、はるかに複雑です。スーパークラスのリストを返すため、スーパークラスまたはスーパークラスとそのサブタイプを知っているクラスに mix メソッドを配置する方がはるかに理にかなっているため、設計を再考することをお勧めします。

本当にこれを行いたい場合は、List2 を List2 にリファクタリングし、次の操作を行う必要があります。

public <R, V extends R> List<R> mix(List<V> list1, List<V> list2) {
    List<R> mixedList;

    mixedList.addAll(list1);
    mixedList.addAll(list2);

    return mixedList;
}
于 2014-01-22T22:34:52.633 に答える