46 Stimmen

Wie kann ich unveränderliche Objekte in Java identifizieren?

In meinem Code erstelle ich eine Sammlung von Objekten, auf die verschiedene Threads auf eine Art und Weise zugreifen, die nur sicher ist, wenn die Objekte unveränderlich sind. Wenn versucht wird, ein neues Objekt in meine Sammlung einzufügen, möchte ich prüfen, ob es unveränderlich ist (wenn nicht, werde ich eine Ausnahme auslösen).

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

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

Damit komme ich eigentlich zu 90 % weiter, aber manchmal wollen meine Benutzer selbst einfache unveränderliche Typen erstellen:

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 mit Reflexion), die ich zuverlässig erkennen könnte, ob eine Klasse unveränderlich ist? Falsch-positive Ergebnisse (denken, dass sie unveränderlich ist, obwohl sie es nicht ist) sind nicht akzeptabel, aber falsch-negative Ergebnisse (denken, dass sie veränderbar ist, obwohl sie es nicht ist) sind es.

Bearbeitet um hinzuzufügen: Vielen Dank für die aufschlussreichen und hilfreichen Antworten. Wie einige der Antworten zeigen, habe ich es versäumt, meine Sicherheitsziele zu definieren. Die Bedrohung besteht hier in ahnungslosen Entwicklern - dies ist ein Stück Framework-Code, das von einer großen Anzahl von Leuten verwendet wird, die so gut wie nichts über Threading wissen und die Dokumentation nicht lesen werden. Ich muss mich NICHT gegen böswillige Entwickler verteidigen - jeder, der clever genug ist, um einen String mutieren oder anderen Blödsinn anstellen, werden auch klug genug sein, um zu wissen, dass es in diesem Fall nicht sicher ist. Die statische Analyse der Codebasis IST eine Option, solange sie automatisiert ist, aber auf Code-Reviews kann man sich nicht verlassen, da es keine Garantie dafür gibt, dass bei jedem Review threading-kundige Reviewer anwesend sind.

32voto

Benno Richters Punkte 14998

Verwenden Sie die Unveränderlich Vermerk von Java-Gleichzeitigkeit in der Praxis . Das Werkzeug FindBugs kann dann helfen, Klassen zu erkennen, die veränderbar sind, es aber nicht sein sollten.

32voto

Simon Lehmann Punkte 10334

Es gibt keine zuverlässige Methode, um festzustellen, ob eine Klasse unveränderlich ist. Das liegt daran, dass es so viele Möglichkeiten gibt, wie eine Eigenschaft einer Klasse geändert werden kann, und man kann sie nicht alle über Reflexion erkennen.

Das ist die einzige Möglichkeit, dem nahe zu kommen:

  • Lassen Sie nur endgültige Eigenschaften von Typen zu, die unveränderlich sind (primitive Typen und Klassen, von denen Sie wissen, dass sie unveränderlich sind),
  • Verlangt, dass die Klasse selbst endgültig ist
  • verlangen, dass sie von einer von Ihnen bereitgestellten Basisklasse erben (die garantiert unveränderbar ist)

Dann können Sie mit folgendem Code prüfen, ob das Objekt unveränderlich ist:

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

Aktualisierung: Wie in den Kommentaren vorgeschlagen, könnte es erweitert werden, um auf die Superklasse zu rekursieren, anstatt nach einer bestimmten Klasse zu suchen. Es wurde auch vorgeschlagen, isImmutable rekursiv in der isValidFieldType-Methode zu verwenden. Dies könnte wahrscheinlich funktionieren, und ich habe auch einige Tests durchgeführt. Aber das ist nicht trivial. Man kann nicht einfach alle Feldtypen mit einem Aufruf von isImmutable prüfen, da String diesen Test bereits nicht besteht (sein Feld hash ist nicht endgültig!). Außerdem kommt es leicht zu endlosen Rekursionen, die zu StackOverflowFehler ;) Andere Probleme können durch Generika verursacht werden, bei denen man auch ihre Typen auf Unveränderlichkeit prüfen muss.

Ich denke, mit etwas Arbeit lassen sich diese potenziellen Probleme irgendwie lösen. Aber dann muss man sich erst einmal fragen, ob es das wirklich wert ist (auch leistungsmäßig).

9voto

Jason Cohen Punkte 78227

In meinem Unternehmen haben wir ein Attribut namens @Immutable . Wenn Sie das an eine Klasse anhängen, bedeutet das, dass Sie versprechen, dass Sie unveränderlich sind.

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

Natürlich hängt es immer noch davon ab, ob der Autor sein Wort hält, dass es unveränderlich ist, aber da der Autor die Anmerkung ausdrücklich hinzugefügt hat, ist das eine vernünftige Annahme.

8voto

SCdF Punkte 53953

Im Grunde genommen nicht.

Man könnte eine riesige Whitelist der akzeptierten Klassen erstellen, aber ich denke, der weniger verrückte Weg wäre, einfach in die Dokumentation für die Sammlung zu schreiben, dass alles, was geht, diese Sammlung ist muss unveränderlich sein.

Editar: Andere haben vorgeschlagen, eine unveränderliche Anmerkung zu verwenden. Das ist in Ordnung, aber man braucht auch die Dokumentation dazu. Andernfalls werden die Leute einfach denken: "Wenn ich diese Annotation in meine Klasse einfüge, kann ich sie in der Sammlung speichern", und sie werden sie einfach in alles einfügen, in unveränderliche und veränderliche Klassen gleichermaßen. In der Tat wäre ich vorsichtig mit einer unveränderlichen Annotation, nur für den Fall, dass die Leute denken, dass die Annotation macht ihre Klasse ist unveränderlich.

4voto

OscarRyz Punkte 189898

Dies könnte ein weiterer Hinweis sein:

Wenn die Klasse keine Setter hat, kann sie nicht verändert werden, da die Parameter, mit denen sie erstellt wurde, entweder "primitive" Typen sind oder selbst nicht veränderbar sind.

Es können auch keine Methoden überschrieben werden, alle Felder sind endgültig und privat,

Ich werde morgen versuchen, etwas für Sie zu programmieren, aber Simons Code mit Reflexion sieht ziemlich gut aus.

Versuchen Sie in der Zwischenzeit, sich ein Exemplar des Buches "Effective Java" von Josh Block zu besorgen, es enthält einen Artikel zu diesem Thema. Es sagt zwar nicht mit Sicherheit, wie man eine nicht veränderbare Klasse erkennt, aber es zeigt, wie man eine gute Klasse erstellt.

Der Artikel wird genannt: "Unveränderlichkeit begünstigen"

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X