Frage

In meinem code erstelle ich eine Sammlung von Objekten, die den Zugriff von verschiedenen threads in einer Weise, die ist nur sicher, wenn die Objekte unveränderlich sind.Wenn ein Versuch unternommen wird, legen Sie ein neues Objekt in meiner Sammlung, ich möchte testen, um zu sehen, wenn es ist unveränderlich (falls nicht, werde ich eine Ausnahme).

Eine Sache, die ich tun kann, ist zu prüfen, ein paar bekannte, unveränderliche Typen:

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());
}

Dieses tatsächlich bekommt mir 90% von dem Weg, aber manchmal meine Benutzer erstellen möchten, die einfache, unveränderliche Typen, Ihre eigenen:

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; }
}

Gibt es eine Möglichkeit (vielleicht durch Reflexion), konnte ich sicher erkennen, ob eine Klasse unveränderlich ist?False-positives " (denken, es ist unveränderlich, wenn es gar nicht) sind nicht akzeptabel, aber falsch-negative Befunde (denken, es ist wandelbar ist, wenn es gar nicht) sind.

Bearbeitet, um hinzufügen: Vielen Dank für das aufschlussreiche und hilfreiche Antworten.Als einige der Antworten, die darauf hinwiesen, dass ich vernachlässigt, um zu definieren, meine Sicherheit Ziele.Die Gefahr hier ist ratlos Entwickler-das ist ein Stück der framework-code, der von einer großen Zahl von Menschen, die wissen, next-to-nichts über threading und nicht das Lesen der Dokumentation.Ich brauche NICHT zu verteidigen gegen böswillige Entwickler -- jemand, der clever genug ist, um mutieren String oder andere Spielereien auch sein, smart genug zu wissen, dass es nicht sicher ist, in diesem Fall.Die statische Analyse von Code-Basis eine option, so lange wie es ist automatisiert, aber code-reviews kann nicht gezählt werden, weil es ist keine Garantie auf jede Kritik haben threading-versierte Gutachter.

War es hilfreich?

Lösung

Es gibt keine zuverlässige Möglichkeit, um herauszufinden, ob eine Klasse unveränderlich ist. Dies ist, weil es so viele Möglichkeiten, eine Eigenschaft einer Klasse geändert werden könnte, und Sie können nicht alle von ihnen durch Reflexion erkennen.

Die einzige Möglichkeit, nahe an diesem zu erhalten ist:

  • Nur Endeigenschaften Typen ermöglichen, die unveränderlich sind (primitive Typen und Klassen, die Sie wissen, sind unveränderlich),
  • Erfordern die Klasse endgültig selbst sein
  • verlangen, dass sie von einer Basisklasse erben Sie zur Verfügung stellen (was unveränderlich sein garantiert ist)

Dann können Sie mit dem folgenden Code überprüfen, ob das Objekt, das Sie haben, ist unveränderlich:

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);
}

Update: Wie in den Kommentaren vorgeschlagen, könnte es auf der übergeordneten Klasse rekursiv erweitert werden, anstatt für eine bestimmte Klasse zu prüfen. Es wurde auch rekursiv isImmutable in der isValidFieldType Methode zu verwenden, vorgeschlagen. Dies könnte wahrscheinlich arbeiten, und ich habe auch einige Tests durchgeführt. Aber das ist nicht trivial. Sie können nicht nur alle Feldtypen überprüfen mit einem Aufruf an isImmutable, weil String bereits versagt dieser Test (sein Feld hash nicht endgültig ist!). Auch sind Sie leicht in eine endlose Rekursion läuft, so dass StackOverflowErrors ;.) Andere Probleme können durch Generika verursacht werden, in dem Sie müssen auch ihre Typen für immutablity überprüfen

Ich denke, mit etwas Arbeit, diese potenziellen Probleme irgendwie gelöst werden könnten. Aber dann muss man sich zuerst fragen, ob es wirklich wert ist (auch Performance weist).

Andere Tipps

Mit dem Java Concurrency in Practice . Das Tool FindBugs dann bei der Aufdeckung von Klassen helfen kann, die wandelbar sind, aber sollte nicht sein.

In meiner Firma haben wir ein Attribut namens @Immutable definiert. Falls Sie Fragen zu einer Klasse anhängen wählen, es bedeutet, dass Sie versprechen Sie unveränderlich sind.

Es funktioniert für die Dokumentation und in Ihrem Fall würde es als Filter arbeitet.

Natürlich sind Sie noch auf dem Autor abhängig sein Wort zu halten über unveränderlich zu sein, aber da der Autor ausdrücklich die Anmerkung hinzugefügt ist es eine vernünftige Annahme.

Im Grunde genommen nicht.

Sie könnten eine riesige weiße-Liste der akzeptierten Klassen bauen, aber ich denke, die weniger verrückt Art und Weise einfach wäre in der Dokumentation für die Sammlung zu schreiben, dass alles, was diese Sammlung geht, ist muss unveränderlich sein.

Edit: Andere Leute haben vorgeschlagen, eine unveränderliche Anmerkung haben. Das ist in Ordnung, aber Sie müssen auch in der Dokumentation. Sonst werden die Leute denken nur, „wenn ich diese Anmerkung in meiner Klasse habe ich es in der Sammlung speichern“ und wird es nur Futter auf alles, gleichermaßen unveränderlich und wandelbar Klassen. In der Tat würde ich nur für den Fall Leute denken, dass Annotation macht ihre Klasse unveränderlich.

vorsichtig sein, eine unveränderliche Anmerkung des Habens

Dies könnte ein weiterer Hinweis sein:

Wenn die Klasse keine Setter hat, dann kann es nicht mutiert werden, gewährt die Parameter es erstellt wurde entweder „primitive“ Typen oder nicht selbst Veränderliche.

Auch konnten keine Methoden außer Kraft gesetzt werden, alle Felder sind endgültig und privat,

Ich werde versuchen, morgen etwas für Sie zu kodieren, aber Simons Code mithilfe von Reflektion sieht ziemlich gut aus.

In der Zwischenzeit versuchen, eine Kopie des „Effective Java“ Buchs von Josh Block-zu packen, es hat einen Artikel zu diesem Thema. Während heißt nicht sicher tut sagen, wie eine inmmutable Klasse zu erkennen, es zeigt, wie eine gute erstellen.

Der Artikel lautet: "Favor Unveränderlichkeit"

Link: http://java.sun.com/docs/books/effective/

  

In meinem Code, ich bin eine Sammlung von Objekten zu schaffen, die durch verschiedene Threads in einer Art und Weise zugegriffen werden, die nur sicher ist, wenn die Objekte unveränderlich sind.

Nicht eine direkte Antwort auf Ihre Frage, aber bedenken Sie, dass Objekte, die unveränderlich sind, werden nicht automatisch sicher garantiert werden Gewinde (leider). Code benötigt Nebeneffekt, frei zu seinen Thread-sicher, und das ist ziemlich viel schwieriger zu sein.

Angenommen, Sie diese Klasse haben:

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

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

Dann wird die foolAround() Methode könnte einige nicht-Thread-sichere Operationen umfasst, die Ihre App sprengen werden. Und es ist nicht möglich, dass diese mit Reflexion zu testen, wie die tatsächliche Referenz nur in der Methode Körper gefunden werden kann, nicht auf den Feldern oder ausgesetzt Schnittstelle.

Other than that, sind die anderen richtig: Sie können für alle deklarierten Felder der Klasse scannen, zu überprüfen, ob jeder von ihnen endgültig ist und auch eine unveränderliche Klasse, und du bist fertig. Ich glaube nicht, Methoden endgültig zu sein, ist eine Anforderung.

Auch vorsichtig sein, rekursiv abhängige Felder für Unveränderlichkeit überprüfen, werden Sie mit Kreisen am Ende:

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

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

Die Klassen A und B perfekt unveränderlich ist (und möglicherweise sogar verwendbar durch einige Reflexion Hacks), aber naiver rekursive Code wird eine Endlosschleife geht in Kontrolle A, dann B, dann wieder A, weiter nach B, ...

Sie können das in Ordnung bringen mit einem ‚gesehen‘ Karte, die Zyklen nicht zulässt, oder mit einigen wirklich cleveren Code, der Klassen unveränderlich sind entscheidet, ob alle ihre dependees nur unveränderlich sind abhängig von selbst, aber das wird wirklich kompliziert sein ...

Sie können Ihre Kunden fragen Metadaten (Anmerkungen) hinzufügen und überprüfen sie zur Laufzeit mit Reflektion, wie folgt aus:

Metadaten:

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

Client-Code:

@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; }
}

Dann durch Reflexion über die Klasse verwenden, prüfen Sie, ob es die Anmerkung hat (ich würde den Code aber seine vorformulierten einfügen und kann leicht online zu finden)

Warum alle Empfehlungen erfordern die Klasse endgültig zu sein? wenn Sie Reflektion verwenden die Klasse jedes Objekts zu überprüfen, und Sie können programmatisch bestimmen, dass diese Klasse unveränderlich (unveränderliche, letzte Felder) ist, dann müssen Sie nicht verlangen, dass die Klasse selbst endgültig ist.

Sie können mit AOP und @Immutable Anmerkung von jcabi-Aspekte :

@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();

Wie die anderen Antwort schon gesagt, IMHO gibt es keine zuverlässige Möglichkeit, herauszufinden, ob ein Objekt ist unveränderlich.

Ich würde nur die Einführung einer Schnittstelle "Unveränderlich" zu überprüfen, wenn anfügen.Dies funktioniert wie ein Tipp, die nur unveränderliche Objekte eingefügt werden soll, für was auch immer Grund, warum Sie es tun.

interface Immutable {}

class MyImmutable implements Immutable{...}

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

Versuchen Sie folgendes:

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;
}

Mein Wissen gibt es keine Möglichkeit unveränderliche Objekte zu identifizieren, die zu 100% korrekt ist. Allerdings habe ich eine Bibliothek geschrieben Sie näher. Es führt Analyse von Bytecode einer Klasse zu bestimmen, ob es unveränderlich ist oder nicht, und zur Laufzeit ausführen kann. Es ist auf der strengen Seite, so dass es ermöglicht auch unveränderliche Klassen bekannt weiße Listen.

Sie können es heraus an: www.mutabilitydetector.org

Es ermöglicht Ihnen, Code wie dies in Ihrer Anwendung zu schreiben:

/*
* 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!
    */
}

Es ist kostenlos und Open Source unter der Apache-2.0-Lizenz.

Schauen Sie sich joe-e , eine Implementierung von Funktionen für Java.

Etwas, was für einen hohen Prozentsatz von gebautet Klassen funktioniert, ist Test für Instanceof vergleichbar. Für die Klassen, die wie Datum nicht unveränderlich sind, werden sie oft als unveränderlich in den meisten Fällen behandelt.

Ich schätze und bewundere die Menge der Arbeit Grundlefleck in seine Veränderlichkeit Detektor gesetzt hat, aber ich denke, es ist ein bisschen zuviel des Guten ist. Sie können einen einfachen schreiben, aber praktisch sehr angemessen (dh, pragmatische ) Detektor wie folgt:

(Anmerkung: Dies ist eine Kopie meines Kommentars hier: https://stackoverflow.com/a/28111150/773113)

Zunächst einmal gehen Sie nicht nur ein Verfahren zu schreiben, die, ob eine Klasse bestimmt ist unveränderlich; Stattdessen müssen Sie eine Unveränderlichkeit Detektor Klasse schreiben, denn es haben wird einigen Zustand zu halten. Der Zustand des Detektors wird die erfasste Unveränderlichkeit aller Klassen, die sie bisher untersucht werden. Dies ist nicht nur nützlich für die Leistung, aber es ist tatsächlich notwendig, weil eine Klasse einen zirkulären Verweis enthält, die ein vereinfach Unveränderlichkeit Detektor verursachen würden in unendliche Rekursion fallen.

Die Unveränderlichkeit einer Klasse hat vier mögliche Werte: Unknown, Mutable, Immutable und Calculating. Sie werden wahrscheinlich wollen eine Karte haben, die jede Klasse zuordnet, die Sie bisher zu einem Unveränderlichkeit Wert begegnet sind. Natürlich Unknown muss nicht tatsächlich umgesetzt werden, da es der implizite Zustand jeder Klasse sein wird, die noch nicht in der Karte ist.

Wenn Sie also eine Klasse beginnen die Prüfung, Sie verbinden es mit einem Calculating Wert in der Karte, und wenn Sie fertig sind, ersetzen Sie Calculating mit entweder Immutable oder Mutable.

Für jede Klasse, müssen Sie nur die Feldmitglieder überprüfen, nicht den Code. Die Idee der Bytecode-Überprüfung ist eher fehl am Platz.

Zunächst einmal sollten Sie nicht prüfen, ob eine Klasse ist endgültig, Die Endgültigkeit einer Klasse hat keinen Einfluss auf seine Unveränderlichkeit. Stattdessen wird ein Verfahren, das zunächst einen unveränderlichen Parameter erwartet sollte rufen Sie die Unveränderlichkeit Detektor die Unveränderlichkeit der Klasse des eigentlichen Objekts zu behaupten, das übergeben wurde. Dieser Test kann entfallen, wenn der Typ des Parameters eine letzte Klasse ist, so Endgültigkeit ist für die Leistung gut, aber streng genommen nicht notwendig. Auch, wie Sie weiter unten sehen werden, ein Feld, dessen Typs einer nicht-final-Klasse wird die deklarierte Klasse dazu führen werden, wie wandelbar in Betracht gezogen, aber immer noch, das ist ein Problem der deklarierte Klasse, nicht das Problem des nicht-Finale unveränderliches Mitglied Klasse. Es ist völlig in Ordnung, eine hohe Hierarchie der unveränderlichen Klassen zu haben, in dem alle Nicht-Blattknoten natürlich nicht endgültig sein muss.

Sie sollten nicht überprüfen, ob ein Feld ist privat; es ist völlig in Ordnung für eine Klasse ein öffentliches Feld zu haben, und die Sichtbarkeit des Feldes nicht die Unveränderlichkeit der deklarierte Klasse in irgendeiner Weise, Form oder Form beeinflussen. Sie müssen nur prüfen, ob das Feld endgültig ist und seine Art ist unveränderlich.

Wenn Sie eine Klasse untersuchen, was man zunächst einmal tun möchte, ist rekursiv die Unveränderlichkeit seiner super Klasse zu bestimmen. Wenn der Super wandelbar ist, dann ist der Nachkomme ist definitions wandelbar zu.

Dann müssen Sie nur überprüfen, die erklärt Felder der Klasse, nicht alle Felder aus.

Wenn ein Feld ist nicht endgültig, dann Klasse wandelbar ist.

Wenn ein Feld endgültig, aber die Art des Feldes ist wandelbar, dann ist Ihre Klasse wandelbar. (Arrays sind definitionsgemäß wandelbar.)

Wenn ein Feld endgültig, und die Art des Feldes ist Calculating, dann ignorieren und zum nächsten Feld gehen. Wenn alle Felder entweder unveränderlich oder Calculating sind, dann Ihre Klasse ist unveränderlich.

Wenn der Typ des Feldes eine Schnittstelle ist, oder eine abstrakte Klasse, oder eine nicht-Finale Klasse, dann ist es, als wandelbar in Betracht gezogen werden, da Sie haben absolut keine Kontrolle darüber, was die tatsächliche Umsetzung tun kann. Dies mag wie ein unüberwindliches Problem scheinen, weil icht bedeutet, dass eine modifizierbare Sammlung innerhalb eines UnmodifiableCollection Verpackung noch den Unveränderlichkeit Test nicht bestehen, aber es ist eigentlich in Ordnung, und es kann mit der folgenden Problemumgehung behandelt werden.

Einige Klassen können nicht endgültige Felder enthalten und noch sein effektiv unveränderlich . Ein Beispiel hierfür ist die String Klasse. Andere Klassen, die in diese Kategorie fallen Klassen sind die Nicht-Endglieder rein für die Leistungsüberwachung Zwecke enthalten (Aufruf Zähler usw.), Klassen, die implementieren Eis am Stiel Unveränderlichkeit (schauen Sie), und Klassen, die enthalten Mitglieder, die Schnittstellen sind, die Nebenwirkungen sind bekannt, um nicht zu verursachen. Auch wenn eine Klasse bona fide veränderbare Felder enthält aber verspricht, sich nicht zu berücksichtigen, wenn hashCode Berechnung () und equals (), dann ist die Klasse natürlich unsicher, wenn es um Threading Multi, aber es kann immer noch in Betracht gezogen werden, wie unveränderlich für den Zweck es als Schlüssel in einer Karte zu verwenden. Also, all diese Fälle in einem von zwei Arten behandelt werden können:

  1. manuell hinzufügen Klassen (und Schnittstellen), um Ihre Unveränderlichkeit Detektor. Wenn Sie wissen, dass eine bestimmte Klasse trotz der Tatsache, effektiv unveränderlich ist, dass die Unveränderlichkeit Test für ihn ausfällt, können Sie manuell einen Eintrag in Ihren Detektor hinzufügen, die es mit Immutable zuordnet. Auf diese Weise wird der Detektor nie zu überprüfen versuchen, ob es unveränderlich ist, wird es immer nur sagen, ‚Ja, es ist.‘

  2. Die Einführung eines @ImmutabilityOverride Anmerkung. Ihr Unveränderlichkeit Detektor kann auf einem Feld für das Vorhandensein dieser Anmerkung überprüfen, und falls vorhanden, kann es das Feld als unveränderlich trotz der Tatsache behandeln, dass das Feld nicht endgültig sein kann oder sein Typ veränderlich sein kann. Der Detektor kann auch auf das Vorhandensein dieser Anmerkung von der Klasse überprüfen, damit die Klasse als unveränderlich Behandlung, ohne auch nur die Mühe seiner Felder zu überprüfen.

Ich hoffe, die zukünftige Generation hilft.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top