3

Java でメソッドのスコープ内に型変数を導入することは可能かどうか疑問に思っています。つまり、メソッド本体内でスコープを制限します。

ただし、問題を抽象的に説明しようとするのではなく、具体的な問題で説明しましょう。次のようなクラスがいくつかあります。

public class Settings {
    public static abstract class Setting<T> {
        public T value;
        public abstract T fallbackvalue();
    }

    private final List<Setting<?>> settings;
}

Settingsここで、すべての設定の値を、抽象メソッドによって提供されるフォールバック値に設定するための関数を に書きたいと思います。私の最初の考えは、次のようにすることです:

public void reset() {
    for(Setting<?> setting : settings)
        setting.value = setting.fallbackvalue();
}

しかし、考え直してみると、なぜこれが機能しないのかは明らかです。setting.valueの のキャプチャは、 のキャプチャと<?>同じではありませんsetting.fallbackvalue()。したがって、キャプチャを統合する方法が必要です。次のように解決できます。

private static <T> void reset1(Setting<T> s) {
    s.value = setting.fallbackvalue();
}

public void reset() {
    for(Setting<?> setting : settings)
        reset1(setting);
}

の明示的な型変数はキャプチャを便利<T>reset1統一しますが、型システムを満たすためだけにこの関数を導入し、名前空間を汚染し、画面を乱雑にし、コードを読みにくくすることは、明らかに世界で最も醜いことです。

の本体内でこれを行う方法はありませんresetか? 私がやりたいことは、単に次のようなものです:

public void reset() {
    for(Setting<?> setting : settings) {
        <T> {
            Setting<T> foo = setting;
            foo.value = foo.fallbackvalue();
        }
    }
}

それは世界で最も美しいものではありませんが、少なくとも私の目には、上記の変種よりもはるかに負担が少ない. 残念ながら、それは不可能です。しかし、何が可能ですか?

4

3 に答える 3

3

コードの他の側面を変更せずに、求めていることを実行する方法はありません。resetただし(特定の問題を解決するために)、内部Settingクラスにメソッドを記述できます。

public void reset() {
    value = fallbackvalue();
}

その場合、(クラスのresetメソッド内の)ループは単純に次のようになります。Settings

for (Setting<?> setting : settings)
    setting.reset();
于 2012-11-15T03:47:56.123 に答える
2

いいえ...

ワイルドカードキャプチャは新しい型変数を導入しますが、それらはコンパイラでのみ使用できます。プログラマーがそれらに直接アクセスする方法はありません。

現在、型変数を導入できるのはクラス/メソッドのみです。したがって、ワイルドカードを含む式をワイルドカードを含まない型に変換する唯一の方法は、メソッド(またはnew Foo<>(setting)本質的に同じメカニズムであるダイアモンド推論を使用するコンストラクター)を介して式を渡すことです。

于 2012-11-15T04:13:04.817 に答える
1

あなたのreset1ことは、それを行う一般的に受け入れられている方法です。これは「キャプチャ ヘルパー」と呼ばれ、ジェネリックでよく引用されるパターンです。通常、次のような状況で発生します。

public void swap(List<?> list, int i, int j) { // swap elements i and j of the list
    // how to write?
}

この場合、パラメータ化された型から何かを取得し、その型に何かを戻す必要があります。ワイルドカードではそれができません。これはあなたの場合と同じです。何かを取り出したり、何かを入れたりしているためです。型が同じでなければならないことはわかっていますが、ワイルドカードは弱すぎてそれを強制できません。明示的な型変数だけがそれを可能にします:

public <T> void swap(List<T> list, int i, int j) {
    T tmp = list.get(i);
    list.set(i, list.get(j));
    list.set(j, tmp);
}

<T>ただし、引数リストの 1 か所だけでしか使用されない、この無関係なものは望ましくありません。外の世界では、swap(List<?> list, int i, int j)完全に正常に動作するはずです。この<T>型パラメーターを使用する必要があることは、誰も知る必要のない実装の詳細です。それを隠すために、ジェネリック関数をワイルドカードを取る関数でラップします。

private <T> void swap_private(List<T> list, int i, int j) { ... }
public void swap(List<?> list, int i, int j) {
    swap_private(list, i, j);
}

無駄に思えますが、そういうことです。

あなたの状況とこれとの間の類似の状況、およびキャプチャヘルパーがこの状況での標準的な解決策であるという事実を考えると、いいえ、それを行うより良い方法はないと自信を持って言えます.

于 2012-11-15T09:43:38.003 に答える