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. constw C++ , finalw 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, mutableaby 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 StringBufferi (mutable version of StringBuilderJava 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ą constantsł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ą readonlyinstrukcji. 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 constinstancji).

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ą mutablesłowa kluczowego, które umożliwia zmianę zmiennej składowej z poziomu constmetody.

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 , consta immutabledla zmiennych, które nie mogą być zmieniane. W przeciwieństwie do C++ const, Javy finali 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 consti immutablejest tym, do czego się odnoszą: constjest 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 immutablejest 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 constlub 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 Cobiektu mutowalnego mFieldmożna do niego zapisywać. Dla const(C)obiektu mFieldnie może być modyfikowany, dziedziczy const; iFieldjest 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 cmoże odnosić się do tego samego obiektu, co m, więc mutacje do mmogą również pośrednio się zmienić c. Ponadto, cmoże odnosić się do tego samego obiektu, jak i, ale ponieważ wtedy wartość jest niezmienna, nie ma żadnych zmian. Jednak mi inie może prawnie odnosić się do tego samego obiektu.

W języku gwarancji mutable nie ma gwarancji (funkcja może zmienić obiekt), constjest tylko zewnętrzną gwarancją, że funkcja niczego nie zmieni i immutablejest dwukierunkową gwarancją (funkcja nie zmieni wartości i osoba wywołująca musi nie zmieniaj tego).

Wartości, które są constlub immutablemuszą być zainicjowane przez bezpośrednie przypisanie w punkcie deklaracji lub przez konstruktora .

Ponieważ constparametry zapominają, czy wartość była zmienna, czy nie, podobna konstrukcja inoutdział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 Sdla zmiennych Targumentó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 stringjest 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ą dupfunkcji.

Jawa

Klasycznym przykładem niezmiennego obiektu jest instancja Stringklasy Java

String s = "ABC";
s.toLowerCase();

Metoda toLowerCase()nie zmienia danych „ABC”, które szawiera. 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 szawierał dane „abc”, potrzebne jest inne podejście:

s = s.toLowerCase();

Teraz String sodwoł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, shortitp.) 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ą finalsłowa kluczowego. finalzapobiega 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 constdeklaracji 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.namedtuplei 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 dataclassesumożliwia programistom emulację niezmienności za pomocą zamrożonych instancji . Jeśli skompilowana zostanie zamrożona klasa danych, dataclassesprzesłoni się __setattr__()i __delattr__()zostanie podniesiona, FrozenInstanceErrorjeś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ą mutsł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 Listi, Mapsą 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