Unveränderliche Klasse?

85

Wie kann man eine Java-Klasse unveränderlich machen, was ist die Notwendigkeit der Unveränderlichkeit und gibt es einen Vorteil, wenn man diese verwendet?

JavaUser
quelle
2
Es gibt eine Einführung für unveränderliche Klasse: javapractices.com/topic/…
qrtt1
Überprüfen Sie diese @ImmutableAnmerkung von jcabi-Aspekten
yegor256
Für zukünftige Besucher - lesen Sie
Hagrawal

Antworten:

134

Was ist ein unveränderliches Objekt?

Ein unveränderliches Objekt ändert seinen Status nicht, nachdem es instanziiert wurde.

Wie mache ich ein Objekt unveränderlich?

Im Allgemeinen kann ein unveränderliches Objekt erstellt werden, indem eine Klasse definiert wird, für die keines ihrer Mitglieder verfügbar ist und für die keine Setter vorhanden sind.

Die folgende Klasse erstellt ein unveränderliches Objekt:

class ImmutableInt {
  private final int value;

  public ImmutableInt(int i) {
    value = i;
  }

  public int getValue() {
    return value;
  }
}

Wie im obigen Beispiel zu sehen ist, kann der Wert von ImmutableIntnur festgelegt werden, wenn das Objekt instanziiert wird, und wenn nur ein Getter ( getValue) vorhanden ist, kann der Status des Objekts nach der Instanziierung nicht geändert werden.

Es muss jedoch darauf geachtet werden, dass alle Objekte, auf die das Objekt verweist, ebenfalls unveränderlich sind. Andernfalls kann der Status des Objekts möglicherweise geändert werden.

Wenn Sie beispielsweise ArrayListzulassen , dass ein Verweis auf ein Array oder über einen Getter abgerufen wird, kann sich der interne Status durch Ändern des Arrays oder der Sammlung ändern:

class NotQuiteImmutableList<T> {
  private final List<T> list;

  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }

  public List<T> getList() {
    return list;
  }
}

Das Problem mit dem obigen Code ist, dass das ArrayListdurch erhalten getListund manipuliert werden kann, was dazu führt, dass der Zustand des Objekts selbst geändert wird, daher nicht unveränderlich.

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

Eine Möglichkeit, dieses Problem zu umgehen, besteht darin, eine Kopie eines Arrays oder einer Sammlung zurückzugeben, wenn diese von einem Getter aufgerufen wird:

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

Was ist der Vorteil der Unveränderlichkeit?

Der Vorteil der Unveränderlichkeit liegt in der Parallelität. Es ist schwierig, die Korrektheit in veränderlichen Objekten aufrechtzuerhalten, da mehrere Threads versuchen könnten, den Status desselben Objekts zu ändern, was dazu führt, dass einige Threads einen unterschiedlichen Status desselben Objekts sehen, abhängig vom Zeitpunkt der Lese- und Schreibvorgänge in das Objekt Objekt.

Durch ein unveränderliches Objekt kann sichergestellt werden, dass alle Threads, die das Objekt betrachten, denselben Status sehen, da sich der Status eines unveränderlichen Objekts nicht ändert.

Coobird
quelle
5
Die Thread-Sicherheit (wichtig für die Parallelität) ist nicht der einzige Vorteil der Unveränderlichkeit. Dies bedeutet auch, dass Sie keine defensiven Kopien von Objekten erstellen müssen, und verhindert Fehler, da Sie Objekte, die nicht geändert werden sollen, nicht versehentlich ändern können.
Jesper
7
Anstatt eine Kopie der Liste zurückzugeben, können Sie auch return Collections.unmodifiableList(list);eine schreibgeschützte Ansicht der Liste zurückgeben.
Jesper
18
Der Unterricht muss auch gemacht finalwerden. Andernfalls kann es mit einer Setter-Methode (oder anderen Arten von Mutationsmethoden und veränderlichen Feldern) erweitert werden.
Abhinav Sarkar
6
@AbhinavSarkar Muss nicht sein, finalwenn die Klasse nur privateFelder hat , da von Unterklassen aus nicht auf sie zugegriffen werden kann.
icza
3
@icza Klasse muss endgültig sein, da wir eine öffentliche Getter-Methode haben, können wir die Klasse erweitern und die Getter-Methode überschreiben und dann das Feld auf unsere Weise ändern. Es ist also nicht mehr unveränderlich
sunil
19

Zusätzlich zu den bereits gegebenen Antworten würde ich empfehlen, über Unveränderlichkeit in Effective Java, 2. Ausgabe, zu lesen, da einige Details leicht zu übersehen sind (z. B. defensive Kopien). Plus, effektive Java 2nd Ed. ist ein Muss für jeden Java-Entwickler.

Fabian Steeg
quelle
3
Dies ist die genaue Ressource, die Sie sich ansehen müssen. Die genannten Vorteile reichen von weniger fehleranfälligem Code bis hin zur Thread-Sicherheit.
Gpampara
6

Sie machen eine Klasse unveränderlich wie folgt:

public final class Immutable
{
    private final String name;

    public Immutable(String name) 
    {
        this.name = name;
    }

    public String getName() { return this.name; } 

    // No setter;
}

Im Folgenden sind die Anforderungen aufgeführt, um eine Java-Klasse unveränderlich zu machen:

  • Klasse muss deklariert werden als final(damit untergeordnete Klassen nicht erstellt werden können)
  • Mitglieder in der Klasse müssen deklariert werden als final(damit wir den Wert nach der Objekterstellung nicht ändern können)
  • Schreiben Sie Getter-Methoden für alle darin enthaltenen Variablen, um Mitgliederwerte abzurufen
  • Keine Setter-Methoden

Unveränderliche Klassen sind nützlich, weil
- sie threadsicher sind.
- Sie drücken auch etwas Tiefes an Ihrem Design aus: "Kann das nicht ändern.", Wenn es zutrifft, ist es genau das, was Sie brauchen.

Duffymo
quelle
5

Unveränderlichkeit kann hauptsächlich auf zwei Arten erreicht werden:

  • Verwenden von finalInstanzattributen, um eine Neuzuweisung zu vermeiden
  • Verwenden einer Klassenschnittstelle, die einfach keine Operation zulässt, mit der die Inhalte Ihrer Klasse geändert werden können (nur Getter und keine Setter)

Vorteile der Unveränderlichkeit sind die Annahmen, die Sie für dieses Objekt treffen können:

  • Sie erhalten die No-Side-Effect-Regel (die in funktionalen Programmiersprachen sehr beliebt ist) und können Objekte in einer gleichzeitigen Umgebung einfacher verwenden, da Sie wissen, dass sie nicht atomar oder nicht atomar geändert werden können, wenn sie vorhanden sind wird von vielen Threads verwendet
  • Implementierungen von Sprachen können diese Objekte auf unterschiedliche Weise behandeln und sie in Speicherzonen platzieren, die für statische Daten verwendet werden. Dies ermöglicht eine schnellere und sicherere Verwendung dieser Objekte (dies geschieht in der JVM für Zeichenfolgen).
Jack
quelle
Unveränderlich ist nicht dasselbe wie gleich nebenwirkungsfrei. Beispielsweise kann ein unveränderliches Objekt Nebenwirkungen wie die Protokollierung in einer Datei hervorrufen. Es ist etwas ungenau zu sagen, dass die Unveränderlichkeit eines Objekts auch nebenwirkungsfrei ist.
Grundlefleck
1
@Grundleflek, ich denke, das kann Haare spalten. Die Klasse ist nicht unveränderlich, wenn sie eine Protokolldatei als Teil ihres Vertrags ändert und diese Protokolldatei für andere Klassen zugänglich ist. Wenn die Protokolldatei vor anderen Klassen verborgen ist und nicht Teil des Klassenvertrags ist, ist die Klasse effektiv unveränderlich und in jeder Hinsicht nebenwirkungsfrei. Die (nicht bezogene) Einführung auf der Wikipedia-Seite mit Nebeneffekten lautet: "... ein Ausdruck soll einen Nebeneffekt haben, wenn er nicht nur einen Wert erzeugt, sondern auch einen bestimmten Status ändert oder eine beobachtbare Interaktion mit aufrufenden Funktionen aufweist."
Jeff Axelrod
3

Unveränderliche Klassen können nach der Instanziierung keine Werte neu zuweisen. Der Konstruktor weist seinen privaten Variablen Werte zu. Bis das Objekt null wird, können Werte aufgrund der Nichtverfügbarkeit von Setter-Methoden nicht geändert werden.

unveränderlich zu sein sollte folgendes befriedigen,

  • Alle Variablen sollten privat sein .
  • Es werden keine Mutatormethoden (Setter) bereitgestellt.
  • Vermeiden Sie das Überschreiben von Methoden, indem Sie die Klasse endgültig (starke Unveränderlichkeit) oder die Methoden endgültig (wöchentliche Unveränderlichkeit) festlegen.
  • Klonen Sie tief, wenn es nicht primitive oder veränderbare Klassen enthält.

/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {

// make the variables private
private String Name;

//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}

//provide getters to access values
public String getName() {

return this.Name;
}
}

Vorteile: Unveränderliche Objekte enthalten ihre initialisierten Werte, bis sie sterben.

Java-unveränderliche-Klassen-Kurznotiz

Kandy
quelle
1

Unveränderliche Klassen sind solche, deren Objekte nach der Erstellung nicht mehr geändert werden können.

Unveränderliche Klassen sind nützlich für

  • Caching-Zweck
  • Gleichzeitige Umgebung (ThreadSafe)
  • Schwer für die Vererbung
  • Der Wert kann in keiner Umgebung geändert werden

Beispiel

String-Klasse

Codebeispiel

public final class Student {
    private final String name;
    private final String rollNumber;

    public Student(String name, String rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public String getName() {
        return this.name;
    }

    public String getRollNumber() {
        return this.rollNumber;
    }
}
Yasir Shabbir Choudhary
quelle
1

@ Jack, endgültige Felder und Setter in der Klasse zu haben, macht die Klasse nicht unveränderlich. Das endgültige Schlüsselwort stellt nur sicher, dass eine Variable niemals neu zugewiesen wird. Sie müssen eine tiefe Kopie aller Felder in Getter-Methoden zurückgeben. Dadurch wird sichergestellt, dass nach dem Abrufen eines Objekts von der Getter-Methode der interne Status des Objekts nicht gestört wird.

Amit Toor
quelle
0

Eine andere Möglichkeit, unveränderliche Objekte zu erstellen, ist die Verwendung der Immutables.org- Bibliothek:

Angenommen, die erforderlichen Abhängigkeiten wurden hinzugefügt, erstellen Sie eine abstrakte Klasse mit abstrakten Zugriffsmethoden. Sie können dasselbe tun, indem Sie mit Schnittstellen oder sogar Anmerkungen (@interface) Anmerkungen machen:

package info.sample;

import java.util.List;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

Es ist jetzt möglich, die generierte unveränderliche Implementierung zu generieren und dann zu verwenden:

package info.sample;

import java.util.List;

public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}

    int foo = value.foo(); // 2

    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}
Genauso wie
quelle
0

Als nicht-englischer Muttersprachler mag ich die übliche Interpretation von "unveränderlicher Klasse" als "konstruierte Klassenobjekte sind unveränderlich" nicht. Vielmehr neige ich selbst dazu, dies als "das Klassenobjekt selbst ist unveränderlich" zu interpretieren.

Das heißt, "unveränderliche Klasse" ist eine Art unveränderliches Objekt. Der Unterschied besteht darin, zu beantworten, was der Nutzen ist. Meines Wissens / meiner Interpretation nach verhindert eine unveränderliche Klasse, dass ihre Objekte Änderungen am Laufzeitverhalten vornehmen.

bbgw
quelle
0

Eine unveränderliche Klasse ist einfach eine Klasse, deren Instanzen nicht geändert werden können.

Alle in jeder Instanz enthaltenen Informationen sind für die Lebensdauer des Objekts festgelegt, sodass niemals Änderungen beobachtet werden können.

Unveränderliche Klassen sind einfacher zu entwerfen, zu implementieren und zu verwenden als veränderbare Klassen.

Befolgen Sie diese fünf Regeln, um eine Klasse unveränderlich zu machen:

  1. Geben Sie keine Methoden an, die den Status des Objekts ändern

  2. Stellen Sie sicher, dass die Klasse nicht erweitert werden kann.

  3. Machen Sie alle Felder endgültig.

  4. Machen Sie alle Felder privat.

  5. Stellen Sie den exklusiven Zugriff auf veränderbare Komponenten sicher.

Unveränderliche Objekte sind von Natur aus threadsicher. Sie erfordern keine Synchronisation.

Unveränderliche Objekte können frei geteilt werden.

Unveränderliche Objekte sind großartige Bausteine ​​für andere Objekte

Sandeep Vanama
quelle
0

Die meisten Antworten hier sind gut, und einige haben die Regeln erwähnt, aber ich finde es gut, in Worte zu fassen, warum und wann wir diese Regeln befolgen müssen. Also gebe ich die folgende Erklärung

  • Deklarieren Sie die Mitgliedsvariablen als 'final' - Wenn wir sie als final deklarieren, zwingt uns der Compiler, sie zu initialisieren. Wir können direkt standardmäßig mit dem Konstruktor arg initialisieren (siehe Beispielcode unten) und nach der Initialisierung können wir sie nicht ändern, da sie endgültig sind.
  • Und wenn wir versuchen, Setter für diese endgültigen Variablen zu verwenden, gibt der Compiler natürlich einen Fehler aus.

    public class ImmutableClassExplored {
    
        public final int a; 
        public final int b;
    
        /* OR  
        Generally we declare all properties as private, but declaring them as public 
        will not cause any issues in our scenario if we make them final     
        public final int a = 109;
        public final int b = 189;
    
         */
        ImmutableClassExplored(){
            this. a = 111;
            this.b = 222;
        }
    
        ImmutableClassExplored(int a, int b){
            this.a = a;
            this.b= b;
        }
    }

Müssen wir die Klasse als "endgültig" deklarieren?

  • Ohne das letzte Schlüsselwort in der Klassendeklaration kann die Klasse vererbt werden. Die Unterklasse kann also Getter-Methoden überschreiben. Hier müssen wir zwei Szenarien betrachten:

1. Nur primitive Mitglieder haben: Wir haben kein Problem Wenn die Klasse nur primitive Mitglieder hat, müssen wir die Klasse nicht als endgültig deklarieren.

2. Objekte als Mitgliedsvariablen haben: Wenn wir Objekte als Mitgliedsvariablen haben, müssen wir die Mitglieder dieser Objekte ebenfalls endgültig machen. Das heißt, wir müssen tief im Baum durchqueren und alle Objekte / Grundelemente als endgültig festlegen, was möglicherweise nicht immer möglich ist. Die Problemumgehung besteht also darin, die Klasse endgültig zu machen, wodurch die Vererbung verhindert wird. Es ist also keine Frage, ob Unterklassen Getter-Methoden überschreiben.

Srikant M.
quelle
0

Wie kann man eine Java-Klasse unveränderlich machen?

Ab JDK 14+ mit JEP 359 können wir " records" verwenden. Es ist die einfachste und stressfreieste Art, unveränderliche Klassen zu erstellen.

Eine Datensatzklasse ist ein flach unveränderlicher , transparenter Träger für einen festen Satz von Feldern, der als Datensatz bezeichnet componentswird und eine stateBeschreibung für den Datensatz enthält. Jedes componentführt zu einem finalFeld, das den angegebenen Wert enthält, und einer accessorMethode zum Abrufen des Werts. Der Feldname und der Zugriffsname stimmen mit dem Namen der Komponente überein.

Betrachten wir das Beispiel zum Erstellen eines unveränderlichen Rechtecks

record Rectangle(double length, double width) {}

Keine Notwendigkeit, einen Konstruktor zu deklarieren, keine Notwendigkeit, equals & hashCode-Methoden zu implementieren. Nur alle Datensätze benötigen einen Namen und eine Statusbeschreibung.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

Wenn Sie den Wert während der Objekterstellung überprüfen möchten, müssen wir den Konstruktor explizit deklarieren.

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

Der Datensatzkörper kann statische Methoden, statische Felder, statische Initialisierer, Konstruktoren, Instanzmethoden und verschachtelte Typen deklarieren.

Instanzmethoden

record Rectangle(double length, double width) {

  public double area() {
    return this.length * this.width;
  }
}

statische Felder, Methoden

Da state Teil der Komponenten sein sollte, können wir Datensätzen keine Instanzfelder hinzufügen. Wir können jedoch statische Felder und Methoden hinzufügen:

record Rectangle(double length, double width) {

  static double aStaticField;

  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}

Was ist das Bedürfnis nach Unveränderlichkeit und hat es einen Vorteil, dies zu nutzen?

Zuvor veröffentlichte Antworten sind gut genug, um die Notwendigkeit der Unveränderlichkeit und ihrer Vorteile zu rechtfertigen

Bhanu Hoysala
quelle