質問

私のコードでは、オブジェクトが不変である場合にのみ安全な方法でさまざまなスレッドによってアクセスされるオブジェクトのコレクションを作成しています。コレクションに新しいオブジェクトを挿入しようとすると、そのオブジェクトが不変かどうかをテストします(そうでない場合は例外をスローします)。

できることの1つは、いくつかの有名な不変の型をチェックすることです:

private static final Set<Class> knownImmutables = new HashSet<Class>(Arrays.asList(
        String.class, Byte.class, Short.class, Integer.class, Long.class,
        Float.class, Double.class, Boolean.class, BigInteger.class, BigDecimal.class
));

...

public static boolean isImmutable(Object o) {
    return knownImmutables.contains(o.getClass());
}

これにより、実際に90%の方法で取得できますが、ユーザーが独自の単純な不変型を作成したい場合があります。

public class ImmutableRectangle {
    private final int width;
    private final int height;
    public ImmutableRectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
    public int getWidth() { return width; }
    public int getHeight() { return height; }
}

クラスが不変かどうかを確実に検出できる方法(おそらくリフレクションを使用する方法)はありますか?偽陽性(そうでない場合は不変だと思う)は受け入れられませんが、偽陰性(そうでない場合は不変だと思う)は受け入れられます。

編集して追加:洞察に満ちた有益な回答をありがとう。いくつかの答えが指摘したように、セキュリティ目標を定義することを怠りました。ここでの脅威は、無知な開発者です。これは、スレッド化についてほとんど知らず、ドキュメントを読まない多くの人々が使用するフレームワークコードです。悪意のある開発者に対して防御する必要はありません。 mutate a文字列または他のシェナンガンを実行することも、この場合は安全ではないことを知るのに十分スマートです。コードベースの静的分析は、自動化されている限りオプションですが、すべてのレビューにスレッドに精通したレビュアーがいるという保証がないため、コードレビューは当てにできません。

役に立ちましたか?

解決

クラスが不変かどうかを検出する信頼できる方法はありません。これは、クラスのプロパティを変更する方法が非常に多く、リフレクションを介してそれらのすべてを検出できないためです。

これに近づく唯一の方法は次のとおりです。

  • 不変の型の最終プロパティのみを許可します(プリミティブ型およびクラスは不変であることを知っています)、
  • クラス自体をfinalにする必要があります
  • 提供する基本クラスから継承する必要があります(不変であることが保証されています)

次に、所有しているオブジェクトが不変であるかどうかを次のコードで確認できます。

static boolean isImmutable(Object obj) {
    Class<?> objClass = obj.getClass();

    // Class of the object must be a direct child class of the required class
    Class<?> superClass = objClass.getSuperclass();
    if (!Immutable.class.equals(superClass)) {
        return false;
    }

    // Class must be final
    if (!Modifier.isFinal(objClass.getModifiers())) {
        return false;
    }

    // Check all fields defined in the class for type and if they are final
    Field[] objFields = objClass.getDeclaredFields();
    for (int i = 0; i < objFields.length; i++) {
        if (!Modifier.isFinal(objFields[i].getModifiers())
                || !isValidFieldType(objFields[i].getType())) {
            return false;
        }
    }

    // Lets hope we didn't forget something
    return true;
}

static boolean isValidFieldType(Class<?> type) {
    // Check for all allowed property types...
    return type.isPrimitive() || String.class.equals(type);
}

更新:コメントで示唆されているように、特定のクラスをチェックするのではなく、スーパークラスで再帰するように拡張できます。 isValidFieldTypeメソッドでisImmutableを再帰的に使用することも提案されました。これはおそらく動作する可能性があり、いくつかのテストも行っています。しかし、これは簡単なことではありません。 isImmutableの呼び出しですべてのフィールドタイプをチェックすることはできません。これは、Stringがすでにこのテストに失敗しているためです(フィールド hash は最終ではありません!)。また、 StackOverflowErrors の原因となる無限の再帰に簡単に遭遇します;)他の問題はジェネリックによって引き起こされる可能性があります。

いくつかの作業で、これらの潜在的な問題は何らかの形で解決されると思います。しかし、それから、本当に価値があるかどうかを最初に自問する必要があります(パフォーマンス面でも)。

他のヒント

a Java同時実行の実践からの「> Immutable 」注釈。ツール FindBugs は、変更可能であるべきではないクラスを検出するのに役立ちます。

私の会社では、 @Immutable という属性を定義しました。これをクラスに添付することを選択した場合、それは不変であることを約束することを意味します。

これは文書化のために機能し、あなたの場合はフィルターとして機能します。

もちろん、あなたはまだ不変であることについて彼の言葉を維持している著者に依存していますが、著者が注釈を明示的に追加したので、それは合理的な仮定です。

基本的にいいえ。

受け入れられたクラスの巨大なホワイトリストを作成できますが、コレクションのドキュメントに書くだけで、このコレクションは不変である必要があります。

編集:他の人々は、不変の注釈を持つことを提案しています。これは問題ありませんが、ドキュメントも必要です。そうでなければ、人々は「クラスにこの注釈を付ければ、コレクションに保存できる」と考えるでしょう。そして、不変で変更可能なクラスに同様にそれをチャックします。実際、注釈がクラスを不変にする と考えられる場合に備えて、不変の注釈を使用することには注意が必要です。

これは別のヒントかもしれません:

クラスにセッターがない場合、クラスを変更することはできず、作成時に使用されたパラメーターは「プリミティブ」または自身がタイプ可能であるかどうか。

メソッドをオーバーライドすることもできません。すべてのフィールドはfinalおよびprivateです。

明日何かをコーディングしようとしますが、リフレクションを使用したSimonのコードはかなり良さそうです。

当面は、「Effective Java」のコピーを入手してください。 Josh Blockの本には、このトピックに関連するアイテムがあります。 isは、不変クラスを検出する方法を明確に言うわけではありませんが、適切なクラスを作成する方法を示しています。

アイテムの呼び出し:&quot; Favor immutability&quot;

リンク: http://java.sun.com/docs/books/effective/

  

コードでは、オブジェクトが不変の場合にのみ安全な方法でさまざまなスレッドからアクセスされるオブジェクトのコレクションを作成しています。

あなたの質問に対する直接的な答えではありませんが、不変のオブジェクトは自動的にスレッドセーフであることが保証されないことに注意してください(悲しいことに)。スレッドセーフであるためには、コードに副作用がないようにする必要がありますが、それはかなり困難です。

このクラスがあるとします:

class Foo {
  final String x;
  final Integer y;
  ...

  public bar() {
    Singleton.getInstance().foolAround();
  }
}

foolAround()メソッドには、スレッドセーフでない操作が含まれている可能性があり、これによりアプリが爆破されます。そして、リフレクションを使用してこれをテストすることはできません。実際の参照は、フィールドまたは公開されたインターフェイスではなく、メソッド本体でのみ見つけることができるためです。

それ以外は正しいです。クラスのすべての宣言されたフィールドをスキャンし、それらのすべてがfinalであり、不変クラスであるかどうかを確認すれば完了です。メソッドが最終的なものである必要はないと思います。

また、依存フィールドの不変性を再帰的にチェックすることに注意してください。円になってしまう可能性があります:

class A {
  final B b; // might be immutable...
}

class B {
  final A a; // same so here.
}

クラスAとBは完全に不変です(そして、おそらくいくつかのリフレクションハックを通じても使用可能です)が、単純な再帰コードはA、B、次にAをチェックして、Bに向かって無限ループに入ります...

サイクルを許可しない「見える」マップ、またはすべての依存先が自分だけに依存して不変である場合にクラスが不変であると判断する本当に賢いコードで修正できますが、それは本当に複雑になります...

メタデータ(注釈)を追加するようにクライアントに依頼し、次のようにリフレクションを使用して実行時にそれらを確認できます。

メタデータ:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CLASS)
public @interface Immutable{ }

クライアントコード:

@Immutable
public class ImmutableRectangle {
    private final int width;
    private final int height;
    public ImmutableRectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
    public int getWidth() { return width; }
    public int getHeight() { return height; }
}

次に、クラスでリフレクションを使用して、アノテーションがあるかどうかを確認します(コードを貼り付けますが、そのボイラープレートは簡単にオンラインで見つけることができます)

すべての推奨事項でクラスを最終的にする必要があるのはなぜですか?リフレクションを使用して各オブジェクトのクラスをチェックし、そのクラスが不変(不変、最終フィールド)であることをプログラムで決定できる場合、クラス自体が最終であることを要求する必要はありません。

AOPおよび @Immutable アノテーションを使用できます jcabi-aspects

@Immutable
public class Foo {
  private String data;
}
// this line will throw a runtime exception since class Foo
// is actually mutable, despite the annotation
Object object = new Foo();

他の回答者がすでに言ったように、私見にはオブジェクトが本当に不変かどうかを確認する信頼できる方法はありません。

インターフェイス&quot; Immutable&quot;を紹介します。追加するときにチェックします。これは、何らかの理由で不変オブジェクトのみを挿入する必要があるというヒントとして機能します。

interface Immutable {}

class MyImmutable implements Immutable{...}

public void add(Object o) {
  if (!(o instanceof Immutable) && !checkIsImmutableBasePrimitive(o))
    throw new IllegalArgumentException("o is not immutable!");
  ...
}

これを試してください:

public static boolean isImmutable(Object object){
    if (object instanceof Number) { // Numbers are immutable
        if (object instanceof AtomicInteger) {
            // AtomicIntegers are mutable
        } else if (object instanceof AtomicLong) {
            // AtomLongs are mutable
        } else {
            return true;
        }
    } else if (object instanceof String) {  // Strings are immutable
        return true;
    } else if (object instanceof Character) {   // Characters are immutable
        return true;
    } else if (object instanceof Class) { // Classes are immutable
        return true;
    }

    Class<?> objClass = object.getClass();

    // Class must be final
    if (!Modifier.isFinal(objClass.getModifiers())) {
            return false;
    }

    // Check all fields defined in the class for type and if they are final
    Field[] objFields = objClass.getDeclaredFields();
    for (int i = 0; i < objFields.length; i++) {
            if (!Modifier.isFinal(objFields[i].getModifiers())
                            || !isImmutable(objFields[i].getType())) {
                    return false;
            }
    }

    // Lets hope we didn't forget something
    return true;
}

私の知る限り、100%正しい不変オブジェクトを識別する方法はありません。しかし、私はあなたを近づけるためにライブラリを書きました。クラスのバイトコードの分析を実行して、クラスが不変かどうかを判断し、実行時に実行できます。厳密な側面にあるため、既知の不変クラスをホワイトリストに登録することもできます。

www.mutabilitydetector.org

で確認できます。

アプリケーションで次のようなコードを記述できます。

/*
* Request an analysis of the runtime class, to discover if this
* instance will be immutable or not.
*/
AnalysisResult result = analysisSession.resultFor(dottedClassName);

if (result.isImmutable.equals(IMMUTABLE)) {
    /*
    * rest safe in the knowledge the class is
    * immutable, share across threads with joyful abandon
    */
} else if (result.isImmutable.equals(NOT_IMMUTABLE)) {
    /*
    * be careful here: make defensive copies,
    * don't publish the reference,
    * read Java Concurrency In Practice right away!
    */
}

Apache 2.0ライセンスの下で無料のオープンソースです。

Javaの機能の実装である joe-e をご覧ください。

組み込みクラスの高い割合で機能するものは、instanceof Comparableのテストです。 Dateのように不変ではないクラスの場合、ほとんどの場合、不変として扱われます。

Grundlefleckが彼の可変性検出器に費やした仕事の量に感謝し、賞賛しますが、それはちょっとやり過ぎだと思います。次のように、シンプルだが実用的に非常に適切な(つまり、 pragmatic )検出器を記述できます。

(注:これは私のコメントのコピーです: https://stackoverflow.com/a/28111150/773113

まず、クラスが不変かどうかを判断するメソッドを記述するだけではありません。代わりに、何らかの状態を維持する必要があるため、不変性検出クラスを作成する必要があります。検出器の状態は、これまでに調べたすべてのクラスの検出された不変性です。これはパフォーマンスに役立つだけでなく、クラスに循環参照が含まれている可能性があるため、実際には必要です。これにより、単純な不変性検出器が無限再帰に陥ります。

クラスの不変性には、 Unknown Mutable Immutable 、および Calculating の4つの値があります。おそらく、これまでに遭遇した各クラスを不変性値に関連付けるマップが必要になるでしょう。もちろん、 Unknown は実際に実装する必要はありません。これは、まだマップにないクラスの暗黙の状態になるためです。

したがって、クラスの調査を開始すると、マップ内の Calculating 値に関連付けられ、完了したら、 Calculating をいずれかの Immutable または Mutable

クラスごとに、コードではなくフィールドメンバーのみをチェックする必要があります。バイトコードをチェックするという考え方は、かなり間違っています。

まず、クラスがfinalであるかどうかをチェックしない必要があります。クラスの最終性は、その不変性に影響しません。代わりに、不変のパラメーターを予期するメソッドは、まず不変検出器を呼び出して、渡された実際のオブジェクトのクラスの不変をアサートする必要があります。パラメータの型が最終クラスの場合、このテストは省略できます。そのため、最終性はパフォーマンスに適していますが、厳密には必要ありません。また、下で見るように、型が非最終クラスのフィールドは、宣言クラスを可変と見なしますが、それでも非宣言クラスの問題ではなく、宣言クラスの問題です不変のメンバークラス。不変クラスの高い階層を持つことはまったく問題ありません。その中で、すべての非リーフノードはもちろん非最終でなければなりません。

フィールドがプライベートであるかどうかをチェックしない。クラスがパブリックフィールドを持つことはまったく問題ありません。フィールドの可視性は、宣言するクラスの不変性、形、または形式に影響しません。フィールドがファイナルで、そのタイプが不変かどうかを確認するだけです。

クラスを調べるとき、まず行うべきことは、再帰してその super クラスの不変性を判断することです。スーパーが変更可能な場合、子孫も定義により変更可能です。

次に、クラスのすべてフィールドではなく、宣言済みフィールドのみをチェックする必要があります。

フィールドがファイナルでない場合、クラスは変更可能です。

フィールドがfinalであるが、フィールドのタイプが可変である場合、クラスは可変です。 (配列は定義により変更可能です。)

フィールドが最終フィールドで、フィールドのタイプが Calculating の場合、それを無視して次のフィールドに進みます。すべてのフィールドが不変または Calculating の場合、クラスは不変です。

フィールドのタイプがインターフェース、抽象クラス、または非最終クラスである場合、実際の実装が何を行うかを完全に制御できないため、可変と見なされます。これは

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top