Obiekt niezmienny - Immutable object
W programowaniu obiektowym i funkcjonalnym obiekt niezmienny ( obiekt niezmienny) to obiekt, którego stan nie może być modyfikowany po jego utworzeniu. Jest to w przeciwieństwie do obiektu mutowalnego ( obiektu zmiennego), który można modyfikować po jego utworzeniu. W niektórych przypadkach obiekt jest uważany za niezmienny, nawet jeśli zmienią się niektóre używane wewnętrznie atrybuty, ale stan obiektu wydaje się niezmienny z zewnętrznego punktu widzenia. Na przykład obiekt, który używa zapamiętywania do buforowania wyników kosztownych obliczeń, nadal może być uważany za obiekt niezmienny.
Ciągi i inne konkretne obiekty są zwykle wyrażane jako niezmienne obiekty w celu poprawy czytelności i wydajności w czasie wykonywania w programowaniu zorientowanym obiektowo . Obiekty niezmienne są również przydatne, ponieważ są z natury bezpieczne wątkowo . Inne korzyści polegają na tym, że są łatwiejsze do zrozumienia i rozumowania oraz oferują wyższe zabezpieczenia niż obiekty mutowalne.
Koncepcje
Niezmienne zmienne
W programowaniu imperatywnym wartości przechowywane w zmiennych programu, których zawartość nigdy się nie zmienia, są znane jako stałe, aby odróżnić je od zmiennych, które można zmienić podczas wykonywania. Przykłady obejmują współczynniki konwersji z metrów na stopy lub wartość pi do kilku miejsc po przecinku.
Pola tylko do odczytu mogą być obliczane podczas działania programu (w przeciwieństwie do stałych, które są znane wcześniej), ale nigdy nie zmieniają się po ich zainicjowaniu.
Słaba vs silna niezmienność
Czasami mówi się o niezmienności pewnych pól przedmiotu. Oznacza to, że nie ma możliwości zmiany tych części stanu obiektu, nawet jeśli inne części obiektu mogą być zmienne ( słabo niezmienne ). Jeśli wszystkie pola są niezmienne, to obiekt jest niezmienny. Jeśli cały obiekt nie może być rozszerzony o inną klasę, obiekt jest nazywany silnie immutable . Może to na przykład pomóc w jawnym wymuszenie pewnych niezmienników dotyczących pewnych danych w obiekcie, które pozostają niezmienione przez cały okres istnienia obiektu. W niektórych językach odbywa się to za pomocą słowa kluczowego (np. const
w C++ , final
w Javie ), które określa pole jako niezmienne. Niektóre języki to odwracają: w OCaml pola obiektu lub rekordu są domyślnie niezmienne i muszą być wyraźnie oznaczone, mutable
aby tak było.
Odniesienia do obiektów
W większości języków obiektowych do obiektów można się odwoływać za pomocą referencji . Niektóre przykłady takich języków to Java , C++ , C# , VB.NET i wiele języków skryptowych , takich jak Perl , Python i Ruby . W takim przypadku ważne jest, czy stan obiektu może się różnić, gdy obiekty są udostępniane za pośrednictwem odwołań.
Odwoływanie się a kopiowanie obiektów
Jeśli wiadomo, że obiekt jest niezmienny, preferowane jest utworzenie odniesienia do niego zamiast kopiowania całego obiektu. Ma to na celu oszczędzanie pamięci poprzez zapobieganie duplikacji danych i unikanie wywołań konstruktorów i destruktorów; powoduje to również potencjalny wzrost szybkości wykonywania.
Technika kopiowania referencji jest znacznie trudniejsza w użyciu w przypadku modyfikowalnych obiektów, ponieważ jeśli jakikolwiek użytkownik modyfikowalnego odwołania do obiektu je zmieni, wszyscy pozostali użytkownicy tego odwołania widzą zmianę. Jeśli nie jest to zamierzony efekt, powiadomienie innych użytkowników w celu uzyskania prawidłowej odpowiedzi może być trudne. W takich sytuacjach kopiowanie obronne całego obiektu, a nie odniesienia, jest zwykle łatwym, ale kosztownym rozwiązaniem. Wzór obserwator jest alternatywą technika do obsługi zmian modyfikowalnych obiektów.
Kopiowanie na piśmie
Techniką, która łączy zalety mutowalnych i niezmiennych obiektów i jest obsługiwana bezpośrednio przez prawie każdy nowoczesny sprzęt, jest kopiowanie przy zapisie (COW). Korzystając z tej techniki, gdy użytkownik prosi system o skopiowanie obiektu, zamiast tego tworzy tylko nowe odniesienie, które nadal wskazuje na ten sam obiekt. Gdy tylko użytkownik spróbuje zmodyfikować obiekt za pomocą określonej referencji, system tworzy prawdziwą kopię, stosuje do niej modyfikację i ustawia referencję tak, aby odnosiła się do nowej kopii. Pozostali użytkownicy pozostają nienaruszeni, ponieważ nadal odwołują się do oryginalnego obiektu. Dlatego w ramach COW wszyscy użytkownicy wydają się mieć zmienną wersję swoich obiektów, chociaż w przypadku, gdy użytkownicy nie modyfikują swoich obiektów, zalety niezmiennych obiektów w zakresie oszczędności miejsca i szybkości są zachowane. Funkcja kopiowania przy zapisie jest popularna w systemach pamięci wirtualnej , ponieważ pozwala im zaoszczędzić miejsce w pamięci, a jednocześnie prawidłowo obsługiwać wszystko, co może zrobić aplikacja.
Staż
Praktyka polegająca na używaniu zawsze referencji zamiast kopii równych obiektów jest znana jako internowanie . Jeśli używany jest interning, dwa obiekty są uważane za równe wtedy i tylko wtedy, gdy ich odniesienia, zwykle reprezentowane jako wskaźniki lub liczby całkowite, są równe. Niektóre języki robią to automatycznie: na przykład Python automatycznie internuje krótkie łańcuchy . Jeśli algorytm implementujący internowanie gwarantuje, że zrobi to w każdym przypadku, w którym jest to możliwe, to porównywanie obiektów pod kątem równości sprowadza się do porównania ich wskaźników – znaczny wzrost szybkości w większości aplikacji. (Nawet jeśli nie ma gwarancji, że algorytm jest kompleksowy, nadal istnieje możliwość poprawy przypadku szybkiej ścieżki, gdy obiekty są równe i używają tego samego odniesienia.) Interning jest ogólnie użyteczny tylko dla obiektów niezmiennych.
Bezpieczeństwo gwintu
Obiekty niezmienne mogą być przydatne w aplikacjach wielowątkowych. Wiele wątków może działać na danych reprezentowanych przez niezmienne obiekty bez obawy o zmianę danych przez inne wątki. Obiekty niezmienne są zatem uważane za bardziej bezpieczne wątkowo niż obiekty zmienne.
Naruszenie niezmienności
Niezmienność nie oznacza, że obiekt przechowywany w pamięci komputera jest niezapisywalny. Niezmienność jest raczej konstrukcją czasu kompilacji, która wskazuje, co programista może zrobić poprzez normalny interfejs obiektu, a niekoniecznie to, co może zrobić absolutnie (na przykład przez obejście systemu typów lub naruszenie poprawności const w C lub C++ ).
Szczegóły specyficzne dla języka
W Python , Java i .NET Framework , ciągi są niezmiennymi obiektami. Zarówno Java, jak i .NET Framework mają zmienne wersje ciągu. W Javie są to StringBuffer
i (mutable version of StringBuilder
Java String
), aw .NET to jest StringBuilder
(mutable version of .Net String
). Python 3 ma zmienny wariant ciągu (bajtów) o nazwie bytearray
.
Ponadto wszystkie prymitywne klasy opakowujące w Javie są niezmienne.
Podobne wzorce to Immutable Interface i Immutable Wrapper .
W czysto funkcjonalnych językach programowania nie jest możliwe tworzenie zmiennych obiektów bez rozszerzania języka (np. poprzez bibliotekę mutowalnych referencji lub obcy interfejs funkcji ), więc wszystkie obiekty są niezmienne.
Ada
W Adzie każdy obiekt jest deklarowany jako zmienny (tj. zmienny; zazwyczaj domyślny domyślny) lub constant
(tj. niezmienny) za pomocą constant
słowa kluczowego.
type Some_type is new Integer; -- could be anything more complicated
x: constant Some_type:= 1; -- immutable
y: Some_type; -- mutable
Parametry podprogramu są niezmienne w w trybie i zmienny w na out i out trybach.
procedure Do_it(a: in Integer; b: in out Integer; c: out Integer) is
begin
-- a is immutable
b:= b + a;
c:= a;
end Do_it;
C#
W C# można wymusić niezmienność pól klasy za pomocą readonly
instrukcji. Wymuszając wszystkie pola jako niezmienne, uzyskujesz niezmienny typ.
class AnImmutableType
{
public readonly double _value;
public AnImmutableType(double x)
{
_value = x;
}
public AnImmutableType Square()
{
return new AnImmutableType(_value * _value);
}
}
C++
W C++ poprawna implementacja constCart
pozwoliłaby użytkownikowi tworzyć instancje klasy, a następnie używać ich jako const
(niezmiennych) lub zmiennych, w zależności od potrzeb, poprzez dostarczenie dwóch różnych wersji items()
metody. (Zauważ, że w C++ nie jest konieczne — a właściwie niemożliwe — zapewnienie wyspecjalizowanego konstruktora dla const
instancji).
class Cart {
public:
Cart(std::vector<Item> items): items_(items) {}
std::vector<Item>& items() { return items_; }
const std::vector<Item>& items() const { return items_; }
int ComputeTotalCost() const { /* return sum of the prices */ }
private:
std::vector<Item> items_;
};
Należy zauważyć, że jeśli istnieje element członkowski danych, który jest wskaźnikiem lub odwołaniem do innego obiektu, możliwe jest zmutowanie obiektu, na który wskazuje lub odwołuje się tylko w ramach metody niestałej.
C++ zapewnia również abstrakcyjną (w przeciwieństwie do bitowej) niezmienność za pomocą mutable
słowa kluczowego, które umożliwia zmianę zmiennej składowej z poziomu const
metody.
class Cart {
public:
Cart(std::vector<Item> items): items_(items) {}
const std::vector<Item>& items() const { return items_; }
int ComputeTotalCost() const {
if (total_cost_) {
return *total_cost_;
}
int total_cost = 0;
for (const auto& item : items_) {
total_cost += item.Cost();
}
total_cost_ = total_cost;
return total_cost;
}
private:
std::vector<Item> items_;
mutable std::optional<int> total_cost_;
};
D
W D , istnieją dwa kwalifikatorów typu , const
a immutable
dla zmiennych, które nie mogą być zmieniane. W przeciwieństwie do C++ const
, Javy final
i C# readonly
, są one przechodnie i rekursywnie stosują się do wszystkiego, co jest osiągalne przez odwołania do takiej zmiennej. Różnica między const
i immutable
jest tym, do czego się odnoszą: const
jest właściwością zmiennej: mogą istnieć prawnie zmienne odniesienia do określonej wartości, tj. wartość może się faktycznie zmienić. W przeciwieństwie do tego immutable
jest właściwością wartości, do której się odnosi: wartość i wszystko, co jest z niej przechodnie osiągalne, nie może się zmienić (bez naruszania systemu typów, co prowadzi do niezdefiniowanego zachowania ). Wszelkie odniesienia do tej wartości muszą być oznaczone const
lub immutable
. Zasadniczo dla każdego typu niekwalifikowanego T
, const(T)
jest rozłącznym połączeniem T
(mutable) i immutable(T)
.
class C {
/*mutable*/ Object mField;
const Object cField;
immutable Object iField;
}
W przypadku C
obiektu mutowalnego mField
można do niego zapisywać. Dla const(C)
obiektu mField
nie może być modyfikowany, dziedziczy const
; iField
jest nadal niezmienna, ponieważ jest silniejszą gwarancją. Dla immutable(C)
, wszystkie pola są niezmienne.
W funkcji takiej jak ta:
void func(C m, const C c, immutable C i)
{ /* inside the braces */ }
Wewnątrz nawiasów c
może odnosić się do tego samego obiektu, co m
, więc mutacje do m
mogą również pośrednio się zmienić c
. Ponadto,
c
może odnosić się do tego samego obiektu, jak i
, ale ponieważ wtedy wartość jest niezmienna, nie ma żadnych zmian. Jednak m
i i
nie może prawnie odnosić się do tego samego obiektu.
W języku gwarancji mutable nie ma gwarancji (funkcja może zmienić obiekt), const
jest tylko zewnętrzną gwarancją, że funkcja niczego nie zmieni i
immutable
jest dwukierunkową gwarancją (funkcja nie zmieni wartości i osoba wywołująca musi nie zmieniaj tego).
Wartości, które są const
lub immutable
muszą być zainicjowane przez bezpośrednie przypisanie w punkcie deklaracji lub przez konstruktora .
Ponieważ const
parametry zapominają, czy wartość była zmienna, czy nie, podobna konstrukcja inout
działa w pewnym sensie jako zmienna dla informacji o zmienności. Funkcja typu const(S) function(const(T))
zwraca const(S)
wpisane wartości dla zmiennych, stałych i niezmiennych argumentów. Natomiast funkcja typu inout(S) function(inout(T))
zwraca S
dla zmiennych T
argumentów, const(S)
dla const(T)
wartości i immutable(S)
dla immutable(T)
wartości.
Rzutowanie niezmiennych wartości na zmienny powoduje niezdefiniowane zachowanie po zmianie, nawet jeśli oryginalna wartość pochodzi ze zmiennego źródła. Rzutowanie wartości zmiennych na niezmienne może być dozwolone, gdy później nie ma już zmiennych odwołań. „Wyrażenie może zostać przekonwertowane z zmiennego (...) na niezmienne, jeśli wyrażenie jest unikatowe i wszystkie wyrażenia, do których odnosi się przechodnie, są unikatowe lub niezmienne”. Jeśli kompilator nie może udowodnić unikalności, rzutowanie może być wykonane jawnie i od programisty zależy, czy nie istnieją żadne zmienne odniesienia.
Typ string
jest aliasem immutable(char)[]
, tj. wpisywanym fragmentem pamięci niezmiennych znaków. Tworzenie podciągów jest tanie, ponieważ po prostu kopiuje i modyfikuje wskaźnik i długość pola oraz jest bezpieczne, ponieważ danych źródłowych nie można zmienić. Obiekty typu const(char)[]
mogą odnosić się do łańcuchów, ale także do buforów mutowalnych.
Tworzenie płytkiej kopii stałej lub niezmiennej wartości usuwa zewnętrzną warstwę niezmienności: Kopiowanie niezmiennego ciągu ( immutable(char[])
) zwraca ciąg ( immutable(char)[]
). Niezmienny wskaźnik i długość są kopiowane, a kopie są zmienne. Wskazane dane nie zostały skopiowane i zachowują swój kwalifikator, w przykładzie immutable
. Można go usunąć, wykonując głębszą kopię, np. za pomocą dup
funkcji.
Jawa
Klasycznym przykładem niezmiennego obiektu jest instancja String
klasy
Java
String s = "ABC";
s.toLowerCase();
Metoda toLowerCase()
nie zmienia danych „ABC”, które s
zawiera. Zamiast tego tworzony jest nowy obiekt String i podczas jego konstruowania otrzymuje dane „abc”. toLowerCase()
Metoda zwraca odwołanie do tego obiektu String . Aby String s
zawierał dane „abc”, potrzebne jest inne podejście:
s = s.toLowerCase();
Teraz String s
odwołuje się do nowego obiektu String, który zawiera „abc”. W składni deklaracji klasy String nie ma nic, co wymuszałoby jej niezmienność; raczej żadna z metod klasy String nigdy nie wpływa na dane zawarte w obiekcie String, co czyni go niezmiennym.
Słowo kluczowe final
( artykuł szczegółowy ) jest używane w implementacji niezmiennych typów pierwotnych i odwołań do obiektów, ale samo w sobie nie może sprawić, że same obiekty będą niezmienne. Zobacz poniżej przykłady:
Zmienne typu pierwotnego ( int
, long
, short
itp.) można ponownie przypisać po zdefiniowaniu. Można temu zapobiec, używając final
.
int i = 42; //int is a primitive type
i = 43; // OK
final int j = 42;
j = 43; // does not compile. j is final so can't be reassigned
Typy referencyjne nie mogą być niezmienne tylko za pomocą final
słowa kluczowego. final
zapobiega jedynie zmianie przydziału.
final MyObject m = new MyObject(); //m is of reference type
m.data = 100; // OK. We can change state of object m (m is mutable and final doesn't change this fact)
m = new MyObject(); // does not compile. m is final so can't be reassigned
Opakowania pierwotne ( Integer
, Long
, Short
, Double
, Float
, Character
, Byte
, Boolean
) również są niezmienne. Klasy niezmienne można zaimplementować postępując zgodnie z kilkoma prostymi wskazówkami.
JavaScript
W JavaScript wszystkie typy podstawowe (Undefined, Null, Boolean, Number, BigInt, String, Symbol) są niezmienne, ale obiekty niestandardowe są generalnie modyfikowalne.
function doSomething(x) { /* does changing x here change the original? */ };
var str = 'a string';
var obj = { an: 'object' };
doSomething(str); // strings, numbers and bool types are immutable, function gets a copy
doSomething(obj); // objects are passed in by reference and are mutable inside function
doAnotherThing(str, obj); // `str` has not changed, but `obj` may have.
Aby zasymulować niezmienność w obiekcie, można zdefiniować właściwości jako tylko do odczytu (zapisywalne: fałsz).
var obj = {};
Object.defineProperty(obj, 'foo', { value: 'bar', writable: false });
obj.foo = 'bar2'; // silently ignored
Jednak powyższe podejście nadal pozwala na dodawanie nowych właściwości. Alternatywnie można użyć Object.freeze, aby uczynić istniejące obiekty niezmiennymi.
var obj = { foo: 'bar' };
Object.freeze(obj);
obj.foo = 'bars'; // cannot edit property, silently ignored
obj.foo2 = 'bar2'; // cannot add property, silently ignored
Dzięki implementacji ECMA262 JavaScript ma możliwość tworzenia niezmiennych referencji, których nie można ponownie przypisać. Jednak użycie const
deklaracji nie oznacza, że wartość odwołania tylko do odczytu jest niezmienna, tylko że nazwa nie może być przypisana do nowej wartości.
const ALWAYS_IMMUTABLE = true;
try {
ALWAYS_IMMUTABLE = false;
} catch (err) {
console.log("Can't reassign an immutable reference.");
}
const arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]
Użycie niezmiennego stanu stało się rosnącym trendem w JavaScript od czasu wprowadzenia React , który faworyzuje wzorce zarządzania stanami podobne do Flux, takie jak Redux .
Perl
W Perlu można utworzyć niezmienną klasę za pomocą biblioteki Moo, po prostu zadeklarowając wszystkie atrybuty tylko do odczytu:
package Immutable;
use Moo;
has value => (
is => 'ro', # read only
default => 'data', # can be overridden by supplying the constructor with
# a value: Immutable->new(value => 'something else');
);
1;
Utworzenie niezmiennej klasy wymagało dwóch kroków: po pierwsze, utworzenia akcesorów (automatycznie lub ręcznie), które uniemożliwiają modyfikację atrybutów obiektów, a po drugie, uniemożliwiają bezpośrednią modyfikację danych instancji instancji tej klasy (zazwyczaj były one przechowywane w hashu referencja i może zostać zablokowana funkcją Hash::Util lock_hash):
package Immutable;
use strict;
use warnings;
use base qw(Class::Accessor);
# create read-only accessors
__PACKAGE__->mk_ro_accessors(qw(value));
use Hash::Util 'lock_hash';
sub new {
my $class = shift;
return $class if ref($class);
die "Arguments to new must be key => value pairs\n"
unless (@_ % 2 == 0);
my %defaults = (
value => 'data',
);
my $obj = {
%defaults,
@_,
};
bless $obj, $class;
# prevent modification of the object data
lock_hash %$obj;
}
1;
Lub za pomocą ręcznie wpisanego akcesora:
package Immutable;
use strict;
use warnings;
use Hash::Util 'lock_hash';
sub new {
my $class = shift;
return $class if ref($class);
die "Arguments to new must be key => value pairs\n"
unless (@_ % 2 == 0);
my %defaults = (
value => 'data',
);
my $obj = {
%defaults,
@_,
};
bless $obj, $class;
# prevent modification of the object data
lock_hash %$obj;
}
# read-only accessor
sub value {
my $self = shift;
if (my $new_value = shift) {
# trying to set a new value
die "This object cannot be modified\n";
} else {
return $self->{value}
}
}
1;
Pyton
W Pythonie niektóre typy wbudowane (liczby, wartości logiczne, łańcuchy, krotki, zamrożone zestawy) są niezmienne, ale klasy niestandardowe są generalnie modyfikowalne. Aby zasymulować niezmienność w klasie, można nadpisać ustawienie i usunięcie atrybutu, aby zgłosić wyjątki:
class ImmutablePoint:
"""An immutable class with two attributes 'x' and 'y'."""
__slots__ = ['x', 'y']
def __setattr__(self, *args):
raise TypeError("Can not modify immutable instance.")
__delattr__ = __setattr__
def __init__(self, x, y):
# We can no longer use self.value = value to store the instance data
# so we must explicitly call the superclass
super().__setattr__('x', x)
super().__setattr__('y', y)
Pomocnicy biblioteki standardowej collections.namedtuple
i typing.NamedTuple
, dostępne od Pythona 3.6 i nowsze, tworzą proste niezmienne klasy. Poniższy przykład jest z grubsza równoważny powyższemu, plus kilka funkcji podobnych do krotek:
from typing import NamedTuple
import collections
Point = collections.namedtuple('Point', ['x', 'y'])
# the following creates a similar namedtuple to the above
class Point(NamedTuple):
x: int
y: int
Wprowadzony w Pythonie 3.7 dataclasses
umożliwia programistom emulację niezmienności za pomocą zamrożonych instancji . Jeśli skompilowana zostanie zamrożona klasa danych, dataclasses
przesłoni się __setattr__()
i __delattr__()
zostanie podniesiona, FrozenInstanceError
jeśli zostanie wywołana.
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
Rakieta
Rakieta zasadniczo odbiega od innych implementacji Schematu , czyniąc jego typ pary rdzeni ("cons cells") niezmienny. Zamiast tego zapewnia równoległy mutowalny typ pary, via mcons
, mcar
, set-mcar!
itp. Ponadto obsługiwanych jest wiele niezmiennych typów, na przykład niezmienne ciągi i wektory, które są szeroko stosowane. Nowe struktury są domyślnie niezmienne, chyba że pole jest wyraźnie zadeklarowane jako zmienne lub cała struktura:
(struct foo1 (x y)) ; all fields immutable
(struct foo2 (x [y #:mutable])) ; one mutable field
(struct foo3 (x y) #:mutable) ; all fields mutable
Język obsługuje również niezmienne tablice mieszające, zaimplementowane funkcjonalnie i niezmienne słowniki.
Rdza
System własności Rusta pozwala programistom deklarować niezmienne zmienne i przekazywać niezmienne referencje. Domyślnie wszystkie zmienne i referencje są niezmienne. Zmienne mutowalne i referencje są tworzone jawnie za pomocą mut
słowa kluczowego.
Przedmioty stałe w Rust są zawsze niezmienne.
// constant items are always immutable
const ALWAYS_IMMUTABLE: bool = true;
struct Object {
x: usize,
y: usize,
}
fn main() {
// explicitly declare a mutable variable
let mut mutable_obj = Object { x: 1, y: 2 };
mutable_obj.x = 3; // okay
let mutable_ref = &mut mutable_obj;
mutable_ref.x = 1; // okay
let immutable_ref = &mutable_obj;
immutable_ref.x = 3; // error E0594
// by default, variables are immutable
let immutable_obj = Object { x: 4, y: 5 };
immutable_obj.x = 6; // error E0596
let mutable_ref2 =
&mut immutable_obj; // error E0596
let immutable_ref2 = &immutable_obj;
immutable_ref2.x = 6; // error E0594
}
Scala
W Scali dowolna encja (wąsko powiązanie) może być zdefiniowana jako zmienna lub niezmienna: w deklaracji można użyć val
(wartość) dla encji niezmiennych i var
(zmienna) dla zmiennych. Należy zauważyć, że nawet jeśli nie można ponownie przypisać niezmiennego powiązania, nadal może ono odwoływać się do modyfikowalnego obiektu i nadal można wywoływać metody mutujące na tym obiekcie: powiązanie jest niezmienne, ale bazowy obiekt może być modyfikowalny.
Na przykład następujący fragment kodu:
val maxValue = 100
var currentValue = 1
definiuje jednostkę niezmienną maxValue
(typ całkowity jest wywnioskowany w czasie kompilacji) i jednostkę zmienną o nazwie currentValue
.
Domyślnie klasy kolekcji, takie jak List
i, Map
są niezmienne, więc metody aktualizacji zwracają nową instancję zamiast mutować istniejącą. Choć może to brzmieć nieefektywnie, implementacja tych klas i ich gwarancje niezmienności sprawiają, że nowa instancja może ponownie wykorzystać istniejące węzły, co szczególnie w przypadku tworzenia kopii jest bardzo wydajne.
Zobacz też
Bibliografia
Ten artykuł zawiera materiały z Perl Design Patterns Book
Zewnętrzne linki
- Niezmienne obiekty w C# w 3 prostych krokach.
- Artykuł Teoria i praktyka Java: mutować czy nie mutować? autorstwa Briana Goetza , z IBM DeveloperWorks – zapisana kopia w Internet Archive autorstwa Briana Goetza , z IBM DeveloperWorks – zapisana kopia w Internet Archive
- Niezmienne obiekty z JavaPractices.com
- Niezmienne obiekty z repozytorium wzorców Portland
- Immutable.js przez Facebook
- Niezmienne struktury w projekcie opensource C# w Codeplex
- Niezmienne kolekcje w oficjalnej bibliotece .NET firmy Microsoft
- Obiekty niezmienne w C# autorstwa Tutlane.com