Jak tworzyć niezmienne obiekty w Javie?

83

Jak tworzyć niezmienne obiekty w Javie?

Które obiekty należy nazwać niezmiennymi?

Jeśli mam klasę ze wszystkimi statycznymi elementami członkowskimi, czy jest ona niezmienna?

Neel Salpe
źródło
możliwy duplikat Co to jest niezmienny?
Joachim Sauer
1
Pytanie powiązane powyżej nie jest tym samym, ale odpowiedzi na to pytanie powinny odpowiedzieć na wszystkie twoje pytania.
Joachim Sauer
Jeśli Twoja klasa zawiera wszystkie statyczne elementy członkowskie, jest bezstanowa (żadna instancja nie ma indywidualnego stanu), a kwestia zmienności lub niezmienności staje się dyskusyjna.
Sebastian Redl
czy istnieje inny sposób inicjalizacji pól niż konstruktor. W mojej klasie mam ponad 20 pól. Zainicjowanie wszystkich pól za pomocą konstruktora jest bardzo trudne, niektóre pola są nawet opcjonalne.
Nikhil Mishra,

Odpowiedzi:

88

Poniżej znajdują się twarde wymagania dotyczące niezmiennego obiektu.

  1. Doprowadź zajęcia do finału
  2. uczyń wszystkie składowe ostatecznymi, ustaw je jawnie, w bloku statycznym lub w konstruktorze
  3. Ustaw wszystkich członków jako prywatne
  4. Brak metod modyfikujących stan
  5. Zachowaj szczególną ostrożność, aby ograniczyć dostęp do zmiennych elementów członkowskich (pamiętaj, że pole może być, finalale obiekt nadal może być zmienny, tj private final Date imStillMutable.). Powinieneś zrobić defensive copiesw takich przypadkach.

Powód stworzenia klasy finaljest bardzo subtelny i często pomijany. Jeśli nie jest to wersja ostateczna, ludzie mogą swobodnie rozszerzać twoją klasę, przesłonić publiclub protectedzachować, dodać zmienne właściwości, a następnie dostarczyć ich podklasę jako substytut. Deklarując klasę, finalmożesz mieć pewność, że tak się nie stanie.

Aby zobaczyć problem w akcji, rozważ poniższy przykład:

public class MyApp{

    /**
     * @param args
     */
    public static void main(String[] args){

        System.out.println("Hello World!");

        OhNoMutable mutable = new OhNoMutable(1, 2);
        ImSoImmutable immutable = mutable;

        /*
         * Ahhhh Prints out 3 just like I always wanted
         * and I can rely on this super immutable class 
         * never changing. So its thread safe and perfect
         */
        System.out.println(immutable.add());

        /* Some sneak programmer changes a mutable field on the subclass */
        mutable.field3=4;

        /*
         * Ahhh let me just print my immutable 
         * reference again because I can trust it 
         * so much.
         * 
         */
        System.out.println(immutable.add());

        /* Why is this buggy piece of crap printing 7 and not 3
           It couldn't have changed its IMMUTABLE!!!! 
         */
    }

}

/* This class adheres to all the principles of 
*  good immutable classes. All the members are private final
*  the add() method doesn't modify any state. This class is 
*  just a thing of beauty. Its only missing one thing
*  I didn't declare the class final. Let the chaos ensue
*/ 
public class ImSoImmutable{
    private final int field1;
    private final int field2;

    public ImSoImmutable(int field1, int field2){
        this.field1 = field1;
        this.field2 = field2;
    }

    public int add(){
        return field1+field2;
    }
}

/*
This class is the problem. The problem is the 
overridden method add(). Because it uses a mutable 
member it means that I can't  guarantee that all instances
of ImSoImmutable are actually immutable.
*/ 
public class OhNoMutable extends ImSoImmutable{   

    public int field3 = 0;

    public OhNoMutable(int field1, int field2){
        super(field1, field2);          
    }

    public int add(){
       return super.add()+field3;  
    }

}

W praktyce powyższy problem jest bardzo często spotykany w środowiskach Dependency Injection. Nie tworzysz instancji w sposób jawny, a otrzymana referencja do superklasy może w rzeczywistości być podklasą.

Wniosek jest taki, że aby uzyskać twarde gwarancje niezmienności, musisz oznaczyć klasę jako final. Jest to szczegółowo omówione w efektywnej Javie Joshua Blocha i wyraźnie przywołane w specyfikacji modelu pamięci Java .

nsfyn55
źródło
co ze wszystkimi statycznymi członkami?
Neel Salpe
1
klasa nie musi być w tym celu ostateczna.
Angel O'Sphere
12
@Nilesh: niezmienność jest własnością instancji . Statyczne elementy członkowskie zwykle nie odnoszą się do żadnej pojedynczej instancji, więc nie pojawiają się tutaj.
Joachim Sauer
4
Pozycja 15 Joshua Blocha dotycząca niezmienności - Brak metod modyfikujących stan, Ostateczne wszystkie pola, Prywatne wszystkie pola, Zapewnienie, że klasa nie może zostać rozszerzona, Zapewnienie wyłącznego dostępu do wszelkich zmiennych komponentów.
nsfyn55,
2
@Jaochim - Absolutnie są częścią równania - Weź powyższy przykład, jeśli dodam zmienny statyczny element członkowski i użyję go w funkcji dodawania ImSoImmutable, masz ten sam problem. Jeśli klasa jest niezmienna, wszystkie aspekty muszą być niezmienne.
nsfyn55,
14

Po prostu nie dodawaj publicznych metod mutatora (ustawiającego) do klasy.

BalusC
źródło
co ze wszystkimi statycznymi członkami? czy odniesienie lub stan obiektu zmienia się dla tego typu obiektów?
Neel Salpe
7
Nieważne. Jeśli nie możesz ich zmienić zewnętrznie jakąś metodą, jest to niezmienne.
BalusC,
na to nie można odpowiedzieć, ponieważ nie wiemy, co robią statyczni członkowie ... często mogli modyfikować pola prywatne. Jeśli to zrobią, klasa nie jest niezmienna.
Angel O'Sphere,
Domyślnym konstruktorem klasy powinien być privatelub klasa powinna być final. Tylko po to, aby uniknąć dziedziczenia. Ponieważ dziedziczenie narusza hermetyzację.
Talha Ahmed Khan
A co z przekazaniem zmiennego obiektu np. Listy do niezmiennego obiektu, a następnie jego zmianą z zewnątrz… jest to możliwe i należy sobie z tym poradzić używając kopii obronnych podczas tworzenia obiektu
Yassin Hajaj
14

Klasy nie są niezmienne, ale obiekty są.

Niezmienne środki: mój stan widoczny publicznie nie może się zmienić po inicjalizacji.

Pola nie muszą być uznawane za ostateczne, chociaż może to bardzo pomóc w zapewnieniu bezpieczeństwa wątków

Jeśli twoja klasa ma tylko statyczne składowe, to obiekty tej klasy są niezmienne, ponieważ nie możesz zmienić stanu tego obiektu (prawdopodobnie nie możesz też go utworzyć :))

Alexander Pogrebnyak
źródło
3
uczynienie wszystkich pól statycznymi ogranicza wszystkie intencje do współdzielenia tego samego stanu, co nie jest naprawdę przydatne.
aviad
6

Aby uczynić klasę niezmienną w języku Java, można zwrócić uwagę na następujące punkty:

1. Nie należy udostępniać metod ustawiających do modyfikowania wartości żadnej ze zmiennych instancji tej klasy.

2. Zadeklaruj klasę jako „ostateczną” . Zapobiegałoby to rozszerzaniu jej przez jakąkolwiek inną klasę, a tym samym zastępowaniu jej metody, która mogłaby modyfikować wartości zmiennych instancji.

3. Zadeklaruj zmienne instancji jako prywatne i ostateczne .

4. Można również zadeklarować konstruktor klasy jako prywatny i w razie potrzeby dodać metodę fabryczną w celu utworzenia instancji klasy.

Te punkty powinny pomóc !!

VishEnthusiast
źródło
3
WRT # 4 Jak widoczność konstruktora wpływa na mutabilność? Ciąg jest niezmienny, ale ma kilka konstruktorów publicznych.
Ryan
Jak powiedział @Ryan, to samo dotyczy zmiennych przykładowych: dlaczego należy je zadeklarować private?
MC Emperor
Nie wiem, dlaczego ta odpowiedź ma jakieś pozytywne głosy. Ta odpowiedź jest niepełna. Nie mówi o zmiennych obiektach, które są dość ważne, aby się nimi zająć. Przeczytaj wyjaśnienie @ nsfyn55 dla lepszego zrozumienia.
Ketan R
4

Z witryny Oracle , jak tworzyć niezmienne obiekty w Javie.

  1. Nie podawaj metod „ustawiających” - metod, które modyfikują pola lub obiekty, do których odwołują się pola.
  2. Spraw, aby wszystkie pola były ostateczne i prywatne.
  3. Nie zezwalaj podklasom na przesłanianie metod. Najprostszym sposobem na to jest zadeklarowanie klasy jako ostatecznej. Bardziej wyrafinowanym podejściem jest uczynienie konstruktora prywatnym i konstruowanie instancji w metodach fabrycznych.
  4. Jeśli pola instancji zawierają odniesienia do obiektów
    modyfikowalnych , nie zezwalaj na ich zmianę: I. Nie udostępniaj metod modyfikujących obiekty podlegające zmianom .
    II. Nie udostępniaj odniesień do zmiennych obiektów. Nigdy nie przechowuj odniesień do zewnętrznych, modyfikowalnych obiektów przekazanych do konstruktora; w razie potrzeby utwórz kopie i przechowuj odniesienia do kopii. Podobnie, w razie potrzeby utwórz kopie swoich wewnętrznych obiektów podlegających zmianom, aby uniknąć zwracania oryginałów w metodach.
e11438
źródło
3

Niezmienny obiekt to taki, który po utworzeniu nie zmieni swojego stanu wewnętrznego. Są bardzo przydatne w aplikacjach wielowątkowych, ponieważ można je udostępniać między wątkami bez synchronizacji.

Aby stworzyć niezmienny obiekt, musisz przestrzegać kilku prostych zasad:

1. Nie dodawaj żadnej metody ustawiającej

Jeśli budujesz niezmienny obiekt, jego stan wewnętrzny nigdy się nie zmieni. Zadaniem metody ustawiającej jest zmiana wartości wewnętrznej pola, więc nie można go dodać.

2. Zadeklaruj wszystkie pola jako ostateczne i prywatne

Pole prywatne nie jest widoczne spoza klasy, więc nie można do niego wprowadzić żadnych ręcznych zmian.

Zadeklarowanie ostatecznej wartości pola zagwarantuje, że jeśli odwołuje się do wartości pierwotnej, wartość nigdy się nie zmieni, jeśli odwołuje się do obiektu, którego odniesienia nie można zmienić. To nie wystarczy, aby zapewnić, że obiekt z tylko prywatnymi polami końcowymi nie będzie zmienny.

3. Jeśli pole jest zmiennym obiektem, utwórz jego kopie obronne dla metod pobierających

Widzieliśmy już wcześniej, że zdefiniowanie pola ostatecznego i prywatnego nie wystarczy, ponieważ można zmienić jego stan wewnętrzny. Aby rozwiązać ten problem, musimy utworzyć kopię obronną tego pola i zwracać to pole za każdym razem, gdy jest ono wymagane.

4. Jeśli zmienny obiekt przekazany do konstruktora musi być przypisany do pola, utwórz jego obronną kopię

Ten sam problem występuje, jeśli trzymasz referencję przekazaną do konstruktora, ponieważ można ją zmienić. Więc trzymanie odniesienia do obiektu przekazanego do konstruktora może tworzyć zmienne obiekty. Aby rozwiązać ten problem, konieczne jest utworzenie kopii obronnej parametru, jeśli są to obiekty zmienne.

Zauważ, że jeśli pole jest odwołaniem do niezmiennego obiektu, nie jest konieczne tworzenie jego kopii obronnych w konstruktorze i metodach pobierających, wystarczy zdefiniować pole jako końcowe i prywatne.

5. Nie zezwalaj podklasom na przesłanianie metod

Jeśli podklasa przesłania metodę, może zwrócić oryginalną wartość zmiennego pola zamiast jego obronnej kopii.

Aby rozwiązać ten problem, można wykonać jedną z następujących czynności:

  1. Zadeklaruj niezmienną klasę jako ostateczną, więc nie można jej rozszerzyć
  2. Zadeklaruj wszystkie metody niezmiennej klasy końcowej, aby nie można było ich zastąpić
  3. Utwórz prywatny konstruktor i fabrykę, aby utworzyć wystąpienia niezmiennej klasy, ponieważ nie można rozszerzyć klasy z prywatnymi konstruktorami

Jeśli zastosujesz się do tych prostych zasad, możesz swobodnie udostępniać swoje niezmienne obiekty między wątkami, ponieważ są one bezpieczne dla wątków!

Poniżej znajduje się kilka ważnych punktów:

  • Niezmienne przedmioty rzeczywiście w wielu przypadkach ułatwiają życie. Są one szczególnie przydatne w przypadku typów wartości, w których obiekty nie mają tożsamości, więc można je łatwo zastąpić i mogą uczynić programowanie współbieżne bezpieczniejszym i czystszym (większość notorycznie trudnych do znalezienia błędów współbieżności jest ostatecznie spowodowanych przez zmienny stan współdzielony między wątków). Jednak w przypadku dużych i / lub złożonych obiektów tworzenie nowej kopii obiektu dla każdej pojedynczej zmiany może być bardzo kosztowne i / lub żmudne . A w przypadku obiektów o wyraźnej tożsamości zmiana istniejących obiektów jest znacznie prostsza i bardziej intuicyjna niż tworzenie ich nowej, zmodyfikowanej kopii.
  • Są rzeczy, których po prostu nie można zrobić z niezmiennymi obiektami, na przykład relacje dwukierunkowe . Po ustawieniu wartości asocjacji na jednym obiekcie zmienia się jego tożsamość. Więc ustawiasz nową wartość na innym obiekcie i ona również się zmienia. Problem polega na tym, że odwołanie do pierwszego obiektu nie jest już poprawne, ponieważ utworzono nową instancję reprezentującą obiekt z odniesieniem. Kontynuowanie tego spowodowałoby po prostu nieskończone regresje.
  • Aby zaimplementować binarne drzewo wyszukiwania , za każdym razem musisz zwrócić nowe drzewo: Twoje nowe drzewo będzie musiało wykonać kopię każdego zmodyfikowanego węzła (niezmodyfikowane gałęzie są udostępniane). W przypadku twojej funkcji wstawiania nie jest to takie złe, ale dla mnie rzeczy szybko stały się dość nieefektywne, kiedy zacząłem pracować nad usuwaniem i ponownym równoważeniem.
  • Hibernacja i JPA zasadniczo dyktują, że twój system używa mutowalnych obiektów, ponieważ cała ich przesłanka polega na tym, że wykrywają i zapisują zmiany w obiektach danych.
  • W zależności od języka kompilator może dokonać wielu optymalizacji podczas pracy z niezmiennymi danymi, ponieważ wie, że dane nigdy się nie zmienią. Pomijane są różne rzeczy, co daje ogromne korzyści w zakresie wydajności.
  • Jeśli spojrzysz na inne znane języki JVM ( Scala, Clojure ), zmienne obiekty są rzadko widoczne w kodzie i dlatego ludzie zaczynają ich używać w scenariuszach, w których pojedynczy wątek nie wystarczy.

Nie ma dobra ani zła, zależy tylko od tego, co wolisz. Zależy to tylko od twoich preferencji i od tego, co chcesz osiągnąć (a możliwość łatwego stosowania obu podejść bez zrażania zagorzałych fanów jednej lub drugiej strony jest świętym Graalem, którego szukają niektóre języki).

amitkumar12788
źródło
2
  • Nie podawaj metod „ustawiających” - metod, które modyfikują pola lub obiekty, do których odwołują się pola.
  • Spraw, aby wszystkie pola były ostateczne i prywatne.
  • Nie zezwalaj podklasom na przesłanianie metod. Najprostszym sposobem na to jest zadeklarowanie klasy jako ostatecznej. Bardziej wyrafinowanym podejściem jest uczynienie konstruktora prywatnym i konstruowanie instancji w metodach fabrycznych.
  • Jeśli pola instancji zawierają odniesienia do obiektów modyfikowalnych, nie zezwalaj na zmianę tych obiektów:
    • Nie podawaj metod, które modyfikują zmienne obiekty.
    • Nie udostępniaj odniesień do zmiennych obiektów. Nigdy nie przechowuj odniesień do zewnętrznych, modyfikowalnych obiektów przekazanych do konstruktora; w razie potrzeby utwórz kopie i przechowuj odniesienia do kopii. Podobnie, w razie potrzeby utwórz kopie swoich wewnętrznych obiektów podlegających zmianom, aby uniknąć zwracania oryginałów w metodach.
Ajay Kumar
źródło
2

Przede wszystkim wiesz, dlaczego musisz stworzyć niezmienny obiekt i jakie są zalety niezmiennego obiektu.

Zalety niezmiennego obiektu

Współbieżność i wielowątkowość Automatycznie zabezpiecza wątki, więc problem z synchronizacją ... itd

Nie trzeba kopiować konstruktora Nie trzeba implementować clone. Nie można przesłonić klasy Uczyń pole jako prywatne i ostateczne elementy wywołujące wymuszanie konstruowania obiektu całkowicie w jednym kroku, zamiast używania konstruktora bez argumentów

Niezmienne obiekty to po prostu obiekty, których stan oznacza, że ​​dane obiektu nie mogą ulec zmianie po skonstruowaniu niezmiennego obiektu.

proszę zobaczyć poniższy kod.

public final class ImmutableReminder{
    private final Date remindingDate;

    public ImmutableReminder (Date remindingDate) {
        if(remindingDate.getTime() < System.currentTimeMillis()){
            throw new IllegalArgumentException("Can not set reminder" +
                    " for past time: " + remindingDate);
        }
        this.remindingDate = new Date(remindingDate.getTime());
    }

    public Date getRemindingDate() {
        return (Date) remindingDate.clone();
    }
}
JBVala
źródło
2

Minimalizuj zmienność

Niezmienna klasa to po prostu klasa, której instancji nie można modyfikować. Wszystkie informacje zawarte w każdej instancji są dostarczane podczas jej tworzenia i są ustalane przez cały okres istnienia obiektu.

Niezmienne klasy JDK: ciąg, zapakowane klasy pierwotne (klasy opakowujące), BigInteger i BigDecimal itp.

Jak uczynić klasę niezmienną?

  1. Nie podawaj żadnych metod modyfikujących stan obiektu (znanych jako mutatory).
  2. Upewnij się, że klasa nie może zostać przedłużona.
  3. Spraw, aby wszystkie pola były ostateczne.
  4. Ustaw wszystkie pola jako prywatne. Zapobiega to uzyskiwaniu przez klientów dostępu do obiektów modyfikowalnych, do których odwołują się pola, i bezpośredniej modyfikacji tych obiektów.
  5. Rób kopie obronne. Zapewnij wyłączny dostęp do wszelkich zmiennych komponentów.

    public List getList () {return Collections.unmodifiableList (list); <=== kopia obronna zmiennego pola przed zwróceniem go dzwoniącemu}

Jeśli Twoja klasa zawiera pola, które odwołują się do obiektów modyfikowalnych, upewnij się, że klienci klasy nie mogą uzyskać odwołań do tych obiektów. Nigdy nie inicjuj takiego pola w odniesieniu do obiektu dostarczonego przez klienta ani nie zwracaj odwołania do obiektu z metody dostępu.

import java.util.Date;
public final class ImmutableClass {

       public ImmutableClass(int id, String name, Date doj) {
              this.id = id;
              this.name = name;
              this.doj = doj;
       }

       private final int id;
       private final String name;
       private final Date doj;

       public int getId() {
              return id;
       }
       public String getName() {
              return name;
       }

     /**
      * Date class is mutable so we need a little care here.
      * We should not return the reference of original instance variable.
      * Instead a new Date object, with content copied to it, should be returned.
      * */
       public Date getDoj() {
              return new Date(doj.getTime()); // For mutable fields
       }
}
import java.util.Date;
public class TestImmutable {
       public static void main(String[] args) {
              String name = "raj";
              int id = 1;
              Date doj = new Date();

              ImmutableClass class1 = new ImmutableClass(id, name, doj);
              ImmutableClass class2 = new ImmutableClass(id, name, doj);
      // every time will get a new reference for same object. Modification in              reference will not affect the immutability because it is temporary reference.
              Date date = class1.getDoj();
              date.setTime(date.getTime()+122435);
              System.out.println(class1.getDoj()==class2.getDoj());
       }
}

Aby uzyskać więcej informacji, zobacz mój blog:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html

Rajesh Dixit
źródło
@Pang czy istnieje inny sposób inicjalizacji pól niż konstruktor. W mojej klasie mam ponad 20 pól. Zainicjowanie wszystkich pól za pomocą konstruktora jest bardzo trudne, niektóre pola są nawet opcjonalne.
Nikhil Mishra,
1
@NikhilMishra, możesz użyć wzorca projektowego Builder, aby zainicjować zmienne podczas konstrukcji obiektu. Możesz zachować obowiązkowe zmienne do ustawienia w konstruktorze, a pozostałe opcjonalne zmienne do ustawienia za pomocą metod ustawiających. Ale mówiąc ściśle, w ten sposób nie stworzysz prawdziwej niezmiennej klasy.
sunny_dev
1

obiekt nazywany jest niezmiennym, jeśli jego stanu nie można zmienić po utworzeniu. Jednym z najprostszych sposobów tworzenia niezmiennej klasy w Javie jest ustawienie wszystkich jej pól jako ostatecznych. Jeśli potrzebujesz napisać niezmienną klasę, która zawiera zmienne klasy, takie jak "java.util.Date". W celu zachowania niezmienności w takich przypadkach zaleca się zwrot kopii oryginalnego przedmiotu,

Rudra
źródło
Nie jest tak, że zalecany jest zwrot kopii, jest to konieczne. Ale konieczne jest również wykonanie kopii obronnej w konstruktorze. W przeciwnym razie obiekt można zmienić w tle.
Ryan
1

Obiekty niezmienne to te obiekty, których stanu nie można zmienić po utworzeniu, na przykład klasa String jest niezmienną klasą. Niezmienne obiekty nie mogą być modyfikowane, więc są również bezpieczne wątkowo podczas wykonywania współbieżnego.

Cechy niezmiennych klas:

  • prosty w budowie
  • automatycznie bezpieczne wątki
  • dobry kandydat na klucze mapy i Ustaw jako ich stan wewnętrzny nie zmieni się podczas przetwarzania
  • nie potrzebują implementacji clone, ponieważ zawsze reprezentują ten sam stan

Klucze do napisania niezmiennej klasy:

  • upewnij się, że klasa nie może zostać zastąpiona
  • ustaw wszystkie zmienne składowe jako prywatne i ostateczne
  • nie podawaj ich metod ustawiających
  • odniesienie do obiektu nie powinno wyciekać na etapie budowy
Manjul
źródło
1

Jeśli chcesz, aby jakakolwiek klasa była niezmienną klasą, należy wziąć pod uwagę kilka poniższych kroków.

  1. Zajęcia należy oznaczyć jako końcowe
  2. Wszystkie pola muszą być prywatne i ostateczne
  3. Zastąp metody ustawiające konstruktorem (do przypisania wartości zmiennej).

Rzućmy okiem na to, co napisaliśmy powyżej:

//ImmutableClass
package younus.attari;

public final class ImmutableExample {

    private final String name;
    private final String address;

    public ImmutableExample(String name,String address){
        this.name=name;
        this.address=address;
    }


    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

}

//MainClass from where an ImmutableClass will be called
package younus.attari;

public class MainClass {

    public static void main(String[] args) {
        ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
        System.out.println(example.getName());

    }
}
user3205589
źródło
0

Powszechnie ignorowane, ale ważne właściwości niezmiennych obiektów

Oprócz odpowiedzi udzielonej przez @ nsfyn55, należy również wziąć pod uwagę następujące aspekty niezmienności obiektu, które mają pierwszorzędne znaczenie

Rozważ następujące klasy:

public final class ImmutableClass {

  private final MutableClass mc;

  public ImmutableClass(MutableClass mc) {
    this.mc = mc;
  }

  public MutableClass getMutClass() {
    return this.mc;
  }
}

public class MutableClass {

  private String name;

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

  public void setName(String name) {
    this.name = name;
  }
}


public class MutabilityCheck {

public static void main(String[] args) {

  MutableClass mc = new MutableClass();

  mc.setName("Foo");

  ImmutableClass iMC = new ImmutableClass(mc);

  System.out.println(iMC.getMutClass().getName());

  mc.setName("Bar");

  System.out.println(iMC.getMutClass().getName());

  }

 }

Poniżej będzie wynik z MutabilityCheck:

 Foo
 Bar

Ważne jest, aby to zauważyć,

  1. Konstruowanie zmiennych obiektów na niezmiennym obiekcie (za pomocą konstruktora) przez „kopiowanie” lub „cloing” do zmiennych instancji elementu niezmiennego opisanego następującymi zmianami:

    public final class ImmutableClass {
    
       private final MutableClass mc;
    
       public ImmutableClass(MutableClass mc) {
         this.mc = new MutableClass(mc);
       }
    
       public MutableClass getMutClass() {
         return this.mc;
       }
    
     }
    
     public class MutableClass {
    
      private String name;
    
      public MutableClass() {
    
      }
      //copy constructor
      public MutableClass(MutableClass mc) {
        this.name = mc.getName();
      }
    
      public String getName() {
        return this.name;
      }
    
      public void setName(String name) {
       this.name = name;
      } 
     }
    

nadal nie zapewnia całkowitej niezmienności, ponieważ z klasy MutabilityCheck nadal obowiązuje:

  iMC.getMutClass().setName("Blaa");
  1. Jednak uruchomienie MutabilityCheck ze zmianami wprowadzonymi w 1. spowoduje, że wynik będzie:

    Foo
    Foo
    
  2. Aby osiągnąć całkowitą niezmienność obiektu, wszystkie jego obiekty zależne również muszą być niezmienne

KJ Sudarshan
źródło
0

Z JDK 14+, który ma JEP 359 , możemy użyć " records". Jest to najprostszy i bezproblemowy sposób tworzenia klasy Immutable.

Klasa rekordu to płytko niezmienny , przezroczysty nośnik dla ustalonego zestawu pól, zwanego rekordem, componentsktóry zawiera stateopis rekordu. Każda z nich componentdaje początek finalpolu, które przechowuje podaną wartość i accessormetodę pobierania wartości. Nazwa pola i nazwa akcesora są zgodne z nazwą komponentu.

Rozważmy przykład tworzenia niezmiennego prostokąta

record Rectangle(double length, double width) {}

Nie ma potrzeby deklarowania żadnego konstruktora, nie ma potrzeby wdrażania equals& hashCodemetod. Dowolne rekordy wymagają nazwy i opisu stanu.

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

Jeśli chcesz sprawdzić poprawność wartości podczas tworzenia obiektu, musimy jawnie zadeklarować konstruktora.

public Rectangle {

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

Treść rekordu może deklarować metody statyczne, pola statyczne, inicjatory statyczne, konstruktory, metody wystąpień i typy zagnieżdżone.

Metody instancji

record Rectangle(double length, double width) {

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

pola statyczne, metody

Ponieważ stan powinien być częścią komponentów, nie możemy dodawać pól instancji do rekordów. Ale możemy dodać statyczne pola i metody:

record Rectangle(double length, double width) {

  static double aStaticField;

  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}
Bhanu Hoysala
źródło