10

列挙型の 3 つのパラメーターを持つコンストラクターがあります。

public SomeClass(EnumType1 enum1,EnumType2 enum2, EnumType3 enum3)
{...}

タイプ enum の 3 つのパラメーターは、可能なすべての値と組み合わせることはできません。

例:

EnumType1.VALUE_ONE、EnumType2.VALUE_SIX、EnumType3.VALUE_TWENTY は有効な組み合わせです。

ただし、次の組み合わせは無効です。

EnumType1.VALUE_TWO、EnumType2.VALUE_SIX、EnumType3.VALUE_FIFTEEN

各 EnumTypes は、どの値と組み合わせることができるかを認識しています。

EnumType1 と他の 2 つは、 isAllowedWith() メソッドを実装して、次のようにチェックします。

public enum EnumType1 {

VALUE_ONE,VALUE_TWO,...;

    public boolean isAllowedWith(final EnumType2 type) {
    switch (this) {
        case VALUE_ONE:
            return type.equals(Type.VALUE_THREE);
        case VALUE_TWO:
            return true;
        case VALUE_THREE:
            return type.equals(Type.VALUE_EIGHT);
        ...
    }
}

私のプロジェクトでは、実行時に組み合わせが常に正しいことが非常に重要であるため、コンパイル時にそのチェックを実行する必要があります。

ユーザー定義の注釈でそのチェックを実行する可能性があるのだろうか?

すべてのアイデアは高く評価されます:)

4

5 に答える 5

4

上記の投稿は、コンパイル時のチェックの解決策をもたらしません。これが私のものです:

ネストされ たの概念を使用しないのはなぜですかEnum

EnumType1独自の値 + ネストされた を含み、これEnumType2はネストされたEnumType3.

便利な組み合わせで全体を整理できます。最終的に 3 つのクラス (EnumType1、2、および 3) と、関連する値のそれぞれに、許可された関連付けられた値を持つ他の値が含まれる可能性があります。

EnumType1.VALUE_ONEそして、あなたの呼び出しはそのようになります(あなたが と関連付けたいと仮定してEnumType2.VALUE_FIFTEEN):

EnumType1.VALUE_ONE.VALUE_FIFTEEN  //second value corresponding to EnumType2

したがって、次のようにすることもできます EnumType3.VALUE_SIX.VALUE_ONE(ここで、SIX は type3 で認識され、ONE は type1 で認識されます)。

あなたの呼び出しは次のように変更されます:

public SomeClass(EnumType1 enumType)

=> サンプル:

SomeClass(EnumType1.VALUE_ONE.VALUE_SIX.VALUE_TWENTY) //being a valid combination as said

より明確にするために、この投稿を確認してください: Usingnested enum types in Java

于 2012-10-16T15:38:57.420 に答える
3

したがって、これを行う最も簡単な方法は、1)有効な組み合わせを説明するドキュメントを定義し、
2)コンストラクターにチェックを追加すること です。

コンストラクターが例外をスローした場合、それは呼び出し側の責任です。基本的に、次のようなことを行います。

public MyClass(enum foo, enum bar, enum baz)  
{  
    if(!validateCombination(foo,bar,baz))
    {  
        throw new IllegalStateException("Contract violated");
    }  
} 


private boolean validateCombination(enum foo, enum bar, enum baz)  
{  
    //validation logic
} 

今、この部分は絶対に重要です。クラスをファイナルとしてマークします。部分的に構築されたオブジェクトを回復して悪用し、アプリケーションを破壊する可能性があります。クラスがfinalとしてマークされていると、悪意のあるプログラムは部分的に構築されたオブジェクトを拡張して大混乱を引き起こすことはできません。

于 2012-10-16T15:21:05.390 に答える
2

別のアイデアの1つは、これをキャッチする自動テストを作成し、アプリをパッケージ化/デプロイする前に、必須のステップとしてそれらをビルドプロセスにフックすることです。

ここで何を捕まえようとしているのかを考えると、それは合法ですが間違っているコードです。コンパイル段階でそれを捕まえることができますが、これはまさにテストの目的です。

これは、ビルドが失敗するため、不正な組み合わせでコードをビルドできないという要件に適合します。そして、間違いなく、他の開発者にとっては、独自の注釈プロセッサを作成するよりも理解しやすいでしょう...

于 2012-10-16T15:58:30.623 に答える
1

私が知っている唯一の方法は、注釈を操作することです。

これが私が意味することです。これで、コンストラクターは 3 つのパラメーターを受け入れます。

public SomeClass(EnumType1 enum1,EnumType2 enum2, EnumType3 enum3){}

したがって、次のように呼び出しています。

SomeClass obj = new SomeClass(EnumTupe1.VALUE1, EnumTupe2.VALUE2, EnumTupe1.VALUE3)

コンストラクターをプライベートに変更します。任意の型の 1 つのパラメーターを受け入れるパブリック コンストラクターを作成します。単なる偽のパラメータである可能性があります。

public SomeClass(Placeholder p)

ここで、各引数に特別な注釈が付けられている間に、このコンストラクターを呼び出す必要があります。それを呼びましょうTypeAnnotation

SomeClass obj = new SomeClass(TypeAnnotation(
    type1=EnumType1.VALUE1, 
    type2=EnumTupe2.VALUE2,
    type3=EnumTupe1.VALUE3)
    p3);

呼び出しはより冗長ですが、これはコンパイル時の検証のために支払わなければならないものです。

では、アノテーションをどのように定義するのでしょうか?

@Documented @Retention({RetentionPolicy.RUNTIME, RetentionPolicy.SOURCE}) @Target(PARAMETER) @interface TypeAnnotation { EnumType1 type1(); EnumType2 type3(); EnumType3 type3(); }

ターゲットが PARAMETER で、保持値が RUNTIME と SOURCE であることに注意してください。

RUNTIME を使用すると、実行時にこの注釈を読み取ることができますが、SOURCE を使用すると、実行時にパラメータを検証できる注釈プロセッサを作成できます。

これで、パブリック コンストラクターが 3 パラメーターのプライベート コンストラクターを呼び出します。

public SomeClass(Placeholder p) { this(readAnnotation(EnumType1.class), readAnnotation(EnumType2.class), readAnnotation(EnumType3.class), ) }

ここでは実装していませんreadAnnotation(): スタック トレースを取得し、(パブリック コストラクタの呼び出し元に) 3 つの要素を戻し、注釈を解析する静的メソッドである必要がありますTypeAnnotation

今が最も興味深い部分です。注釈プロセッサを実装する必要があります。手順についてはこちらを、注釈プロセッサの例についてはこちらをご覧ください。

このアノテーション プロセッサの使用をビルド スクリプトと (オプションで) IDE に追加する必要があります。この場合、互換性ルールに違反すると、実際のコンパイル エラーが発生します。

このソリューションは複雑すぎるように見えますが、本当に必要な場合はこれを行うことができます。1日前後かかる場合がございます。幸運を。

于 2012-10-16T18:04:23.773 に答える
0

まあ、私はコンパイル時のチェックを認識していませんが、コンストラクターに渡される値をコンパイラーがどのように知ることができるか (列挙型変数の値が実行時に計算される場合 (たとえば、 If 句) ? これは、列挙型に対して実装したバリデータ メソッドを使用して実行時にのみ検証できます。

例 :

コードに次のようなものがある場合:

EnumType1 enumVal;

if (<some condition>) {
  enumVal = EnumType2.VALUE_SIX;
} else {
  enumVal = EnumType2.VALUE_ONE;
}

どの値が enumVal に割り当てられるかをコンパイラーが知る方法はないため、if ブロックが評価されるまでコンストラクターに渡されたものを確認できません (これは実行時にのみ実行できます)。

于 2012-10-16T15:23:14.353 に答える