W jaki sposób można uczynić niezmienną klasę Javy, jaka jest potrzeba niezmienności i czy jest jakaś korzyść z jej używania?
W jaki sposób można uczynić niezmienną klasę Javy, jaka jest potrzeba niezmienności i czy jest jakaś korzyść z jej używania?
Odpowiedzi:
Co to jest niezmienny obiekt?
Niezmienny obiekt to taki, który nie zmieni stanu po utworzeniu wystąpienia.
Jak uczynić obiekt niezmiennym?
Ogólnie rzecz biorąc, niezmienny obiekt można utworzyć, definiując klasę, która nie ma ujawnionego żadnego ze swoich elementów członkowskich i nie ma żadnych metod ustawiających.
Następująca klasa utworzy niezmienny obiekt:
class ImmutableInt {
private final int value;
public ImmutableInt(int i) {
value = i;
}
public int getValue() {
return value;
}
}
Jak widać w powyższym przykładzie, wartość the ImmutableInt
można ustawić tylko wtedy, gdy obiekt jest tworzony, a mając tylko metodę getter ( getValue
), stan obiektu nie może zostać zmieniony po utworzeniu instancji.
Należy jednak uważać, aby wszystkie obiekty, do których odwołuje się obiekt, również były niezmienne, w przeciwnym razie może istnieć możliwość zmiany stanu obiektu.
Na przykład zezwolenie na odwołanie do tablicy lub ArrayList
uzyskanie go za pomocą metody pobierającej pozwoli na zmianę stanu wewnętrznego przez zmianę tablicy lub kolekcji:
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;
}
}
Problem z powyższym kodem polega na tym, że ArrayList
można go uzyskać getList
i manipulować nim, co prowadzi do zmiany stanu samego obiektu, a zatem nie jest niezmienny.
// 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");
Jednym ze sposobów obejścia tego problemu jest zwrócenie kopii tablicy lub kolekcji po wywołaniu z metody pobierającej:
public List<T> getList() {
// return a copy of the list so the internal state cannot be altered
return new ArrayList(list);
}
Jaka jest zaleta niezmienności?
Zaletą niezmienności jest współbieżność. Trudno jest zachować poprawność w obiektach zmiennych, ponieważ wiele wątków może próbować zmienić stan tego samego obiektu, co prowadzi do tego, że niektóre wątki widzą inny stan tego samego obiektu, w zależności od czasu odczytów i zapisów do wspomnianego obiekt.
Mając niezmienny obiekt, można zapewnić, że wszystkie wątki, które patrzą na obiekt, będą widzieć ten sam stan, ponieważ stan niezmiennego obiektu nie ulegnie zmianie.
return Collections.unmodifiableList(list);
zwrócić na liście widok tylko do odczytu.
final
. W przeciwnym razie można go rozszerzyć o metodę ustawiającą (lub inne rodzaje metod mutujących i pól mutowalnych).
final
jeśli klasa ma tylko private
pola, ponieważ nie można uzyskać do nich dostępu z podklas.
Oprócz odpowiedzi już udzielonych, polecam przeczytanie o niezmienności w Effective Java, wyd. 2, ponieważ jest kilka szczegółów, które łatwo przeoczyć (np. Kopie obronne). Dodatkowo Effective Java 2nd Ed. to lektura obowiązkowa dla każdego programisty Java.
Tworzysz niezmienną klasę w następujący sposób:
public final class Immutable
{
private final String name;
public Immutable(String name)
{
this.name = name;
}
public String getName() { return this.name; }
// No setter;
}
Poniżej przedstawiono wymagania, aby uczynić niezmienną klasę Javy:
final
(aby nie można było tworzyć klas podrzędnych)final
(abyśmy nie mogli zmienić jej wartości po utworzeniu obiektu)Niezmienne klasy są przydatne, ponieważ
- są bezpieczne dla wątków.
- Wyrażają również coś głębokiego w Twoim projekcie: „Nie można tego zmienić”. Kiedy ma zastosowanie, jest dokładnie tym, czego potrzebujesz.
Niezmienność można osiągnąć głównie na dwa sposoby:
final
atrybutów instancji, aby uniknąć ponownego przypisaniaZaletami niezmienności są założenia, które możesz poczynić na tym obiekcie:
Niezmienne klasy nie mogą ponownie przypisać wartości po utworzeniu instancji. Konstruktor przypisuje wartości do swoich prywatnych zmiennych. Dopóki obiekt nie stanie się pusty, wartości nie mogą być zmieniane z powodu niedostępności metod ustawiających.
być niezmiennym powinno spełniać następujące,
/**
* 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;
}
}
Zalety: Niezmienne obiekty zawierają zainicjalizowane wartości, dopóki nie umrą.
Niezmienne klasy to takie, których obiektów nie można zmienić po utworzeniu.
Niezmienne klasy są przydatne dla
Przykład
Klasa ciągów
Przykład kodu
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;
}
}
W jaki sposób można uczynić niezmienną klasę Javy?
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, components
który zawiera state
opis rekordu. Każda z nich component
daje początek final
polu, które przechowuje podaną wartość i accessor
metodę pobierania wartości. Nazwa pola i nazwa akcesorium 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 implementowania metod equals i hashCode. 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");
}
}
jaka jest potrzeba niezmienności i czy jest jakaś korzyść z jej używania?
Wcześniej opublikowane odpowiedzi są wystarczająco dobre, aby uzasadnić potrzebę niezmienności i są to zalety
Innym sposobem na stworzenie niezmiennego obiektu jest użycie biblioteki Immutables.org :
Zakładając, że zostały dodane wymagane zależności, utwórz klasę abstrakcyjną z abstrakcyjnymi metodami dostępu. Możesz zrobić to samo, dodając adnotacje za pomocą interfejsów, a nawet adnotacji (@interface):
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();
}
Teraz można wygenerować, a następnie użyć wygenerowanej niezmiennej implementacji:
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)
}
}
Niezmienna klasa to po prostu klasa, której instancji nie można modyfikować.
Wszystkie informacje zawarte w każdej instancji są ustalone przez cały okres istnienia obiektu, więc nie można zaobserwować żadnych zmian.
Niezmienne klasy są łatwiejsze do projektowania, implementowania i używania niż klasy mutowalne.
Aby uczynić klasę niezmienną, przestrzegaj pięciu zasad:
Nie podawaj metod modyfikujących stan obiektu
Upewnij się, że klasa nie może zostać przedłużona.
Spraw, aby wszystkie pola były ostateczne.
Ustaw wszystkie pola jako prywatne.
Zapewnij wyłączny dostęp do wszelkich zmiennych komponentów.
Niezmienne obiekty są z natury bezpieczne dla wątków; nie wymagają synchronizacji.
Niezmienne obiekty można swobodnie udostępniać.
Niezmienne obiekty są świetnymi cegiełkami do budowania innych obiektów
@Jack, Posiadanie końcowych pól i seterów w klasie nie spowoduje, że klasa będzie niezmienna. końcowe słowo kluczowe zapewnia tylko, że zmienna nigdy nie zostanie ponownie przypisana. Musisz zwrócić głęboką kopię wszystkich pól w metodach pobierających. Zapewni to, że po pobraniu obiektu z metody getter stan wewnętrzny obiektu nie zostanie zakłócony.
Jako osoba, dla której angielski nie jest językiem ojczystym, nie podoba mi się powszechna interpretacja istoty „niezmiennej klasy”, „skonstruowane obiekty klas są niezmienne”; raczej skłaniam się do interpretowania tego jako „sam obiekt klasy jest niezmienny”.
To powiedziawszy, „niezmienna klasa” jest rodzajem niezmiennego obiektu. Różnica polega na udzieleniu odpowiedzi na pytanie, jakie są korzyści. Zgodnie z moją wiedzą / interpretacją, niezmienna klasa zapobiega modyfikacjom zachowania swoich obiektów w czasie wykonywania.
Większość odpowiedzi tutaj jest dobrych, a niektórzy wspominali o zasadach, ale czuję, że dobrze jest opisać słowami, dlaczego i kiedy musimy ich przestrzegać. Więc podaję poniższe wyjaśnienie
I oczywiście, jeśli spróbujemy użyć Setterów dla tych końcowych zmiennych, kompilator zgłosi błąd.
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;
}
}
Czy musimy zadeklarować klasę jako „ostateczną”?
1. Posiadanie tylko prymitywów: nie mamy problemu Jeśli klasa ma tylko prymitywne składowe, to nie musimy deklarować klasy jako ostatecznej.
2.Mając obiekty jako zmienne składowe: Jeśli mamy obiekty jako zmienne składowe, to musimy uczynić składowe tych obiektów również ostatecznymi. Oznacza to, że musimy przejść głęboko w drzewo i uczynić wszystkie obiekty / prymitywy ostatecznymi, co może nie być możliwe za każdym razem. Tak więc obejściem jest uczynienie klasy ostateczną, co zapobiega dziedziczeniu. Nie ma więc kwestii przesłaniania metod pobierających przez podklasy.
Adnotacja @Value Lombok może służyć do generowania niezmiennych klas. To tak proste, jak poniższy kod.
@Value
public class LombokImmutable {
int id;
String name;
}
Zgodnie z dokumentacją na stronie Lombok:
@Value jest niezmiennym wariantem @Data; wszystkie pola są domyślnie ustawiane jako prywatne i ostateczne, a metody ustawiające nie są generowane. Sama klasa jest również domyślnie określana jako ostateczna, ponieważ niezmienność nie jest czymś, co można wymusić na podklasie. Podobnie jak @Data, generowane są również przydatne metody toString (), equals () i hashCode (), każde pole otrzymuje metodę pobierającą, a także generowany jest konstruktor obejmujący każdy argument (z wyjątkiem pól końcowych, które są inicjowane w deklaracji pola) .
W pełni działający przykład można znaleźć tutaj.
record
można używać Javy ?