3

私は次のように定義された1つのスーパータイプを持っています:

public abstract class AType<T> {
        ....

    private T value;
    private T mask;

    public T getValue() {
        if (isMasking())
            return null;

        return this.value;
    }

    public void setValue(T value) {
        if (value == null)
            throw new IllegalArgumentException("Value is mandatory.");

        this.value = value;
    }

    protected T getMask() {
        if (!isMasking())
            return null;

        return this.mask;
    }

    protected void setMask(T mask) {
        if (mask == null)
            throw new IllegalArgumentException("Mask is mandatory.");

        this.setMasking(true);
        this.mask = mask;
    }
        ...
}

そして次のようないくつかのサブタイプ:

public class SpecType extends AType<Integer> {
    ...
}

これらのサブタイプは、不明なパラメータを指定します。IPv4、Longなどがあります。

今、私はどういうわけか実行時に動的キャストを行う必要があります...私はこれらのクラスを次のように列挙型で定義しています:

public enum Type {
    SOME_TYPE(new TypeID(0, (short) 0), OFMU16.class,
            new Instantiable<AType<?>>() {
                @Override
                public SpecType instantiate() {
                    return new SpecType(new OFMatchTypeIdentifier(0, (short) 0));
                }
            }),...;

    ...

    public Class<? extends AType<?>> toClass() {
    return this.clazz;
}

    ...

}

私は次のようなことをしたい:

AType<?> type = SOME_TYPE.newInstance();    //this works

SOME_TYPE.toClass().cast(type).setValue(10);    //this don't work

だから私はそれを静的に行わなければなりません:

((SpecType) type).setValue(10);

すべてがOKですが、このモジュールのユーザーは、列挙型を調べて毎回手動でキャストすることを望まないでしょう。これはおそらく間違いを犯し、デバッグに多くの時間を費やします:/...。

私の質問は、これをリファクタリングするにはどうすればよいですか、またはユーザーが動的にキャストできるように継承の構造を定義するにはどうすればよいですか?出来ますか?

編集: ネットワークからのパケットを解析しています。ベンダータイプ識別子と値/マスクのタイプが異なる多くのタイプがあります-これらのフィールドはすべてこの組み合わせに対して一定であるため、列挙型定数として定義しました。Fe 20はTypeIDのみが異なりますが、VendorIDは同じであり、すべて整数として表すことができます。次の10はVendorIDとTypeIDが異なりますが、Shortなどとして表すことができます。

4

2 に答える 2

2

なぜキャストしなければならないのかはまだ明らかではありません。SOME_TYPEがソースコードに書き込まれるか、set メソッドのタイプがハードコーディングされるとすぐに(例または)、実行時チェックは必要ありません。コンパイル時のチェックが必要です。setValueintInteger

したがって、次のスニペットは、API ユーザーがどのようにコーディングする必要があるかを示していると思います。

public class TypeTest {
    public static void main(String[] args) {
        AType<Integer> type0 = Types.SOME_TYPE_0.instantiate();
        type0.setValue(10);

        AType<String> type1 = Types.SOME_TYPE_1.instantiate();
        type1.setValue("foo");
    }
}

Generics の部分を理解するために必要な最小限の例を削除しました。

abstract class AType<T> {
    private T value;

    // standard getter/setter
    public T getValue() { return this.value; }
    public void setValue(T value) { this.value = value; }
}

class SpecTypeInt extends AType<Integer> {
}

class SpecTypeString extends AType<String> {
}

interface Instantiable<T> {
    T instantiate();
}

重要な部分は次のとおりです。 は型パラメーターを持つことができないenumため、を使用しないでください。次のスニペットのように、代わりにenumプレーンを使用できます。interfaceインターフェイス内の各参照は、ファクトリを指しています。各ファクトリは、a) 抽象型と b) 具象型を認識しています。Generics を満足させるには、a) と b) を で接着する必要があります? extends X

interface Types {
    Instantiable<? extends AType<Integer>> SOME_TYPE_0 = new Instantiable<SpecTypeInt>() {
        @Override
        public SpecTypeInt instantiate() {
            return new SpecTypeInt();
        }
    };

    Instantiable<? extends AType<String>> SOME_TYPE_1 = new Instantiable<SpecTypeString>() {
        @Override
        public SpecTypeString instantiate() {
            return new SpecTypeString();
        }
    }  ;
}

クリーンアップ:ユーザーはインターフェイスを調べる必要があります: はい、どのような場合でも必要setValue です。これを回避できるソリューションはありません。Eclipse はあなたとあなたのユーザーを少し助けるかもしれません:main入力するだけTypes.SOME_TYPE_1.instantiate();で行の先頭に移動し、Ctrl2+ L(「ローカル変数に割り当て」) を押すと、Eclipse がそのAType<String> instantiate =部分を置き換えます。


1ユーザーがメソッドの適切な型を知らない場合setValueは、間違った質問をしています。その場合、「ジェネリックの安全な変換機能を設計するにはどうすればよいですか?」のような質問をするべきでした。

于 2012-08-01T16:39:07.127 に答える
0

おそらく、次のような setValue メソッドを使用します。

public void setValue(Object value) {
    if (value == null)
        throw new IllegalArgumentException("Value is mandatory.");
    this.value = (T)value;
}

あなたはチェックされていないキャストを持っていますが。

お役に立てれば

于 2012-08-01T11:41:32.380 に答える