Oföränderligt objekt

I objektorienterad och funktionell programmering är ett oföränderligt objekt (oföränderligt objekt) ett objekt vars tillstånd inte kan ändras efter att det skapats. Detta till skillnad från ett föränderligt objekt (föränderligt objekt), som kan modifieras efter att det har skapats. I vissa fall anses ett objekt vara oföränderligt även om vissa internt använda attribut ändras, men objektets tillstånd verkar oföränderligt utifrån en extern synvinkel. Till exempel kan ett objekt som använder memoisering för att cachelagra resultaten av dyra beräkningar fortfarande betraktas som ett oföränderligt objekt.

Strängar och andra konkreta objekt uttrycks vanligtvis som oföränderliga objekt för att förbättra läsbarheten och körtidseffektiviteten i objektorienterad programmering . Oföränderliga objekt är också användbara eftersom de i sig är trådsäkra . Andra fördelar är att de är enklare att förstå och resonera kring och erbjuder högre säkerhet än föränderliga objekt.

Begrepp

Oföränderliga variabler

I imperativ programmering kallas värden i programvariabler vars innehåll aldrig ändras som konstanter för att skilja dem från variabler som kan ändras under exekvering. Exempel inkluderar omvandlingsfaktorer från meter till fot, eller värdet av pi till flera decimaler.

Skrivskyddade fält kan beräknas när programmet körs (till skillnad från konstanter, som är kända i förväg), men ändras aldrig efter att de initierats.

Svag vs stark oföränderlighet

Ibland talar man om att vissa fält av ett objekt är oföränderliga. Detta innebär att det inte finns något sätt att ändra dessa delar av objektets tillstånd, även om andra delar av objektet kan vara föränderliga ( svagt oföränderliga ) . Om alla fält är oföränderliga är objektet oföränderligt. Om hela objektet inte kan utökas med en annan klass kallas objektet starkt oföränderligt . Detta kan till exempel hjälpa till att explicit tvinga fram vissa invarianter om att vissa data i objektet förblir oförändrade under objektets livstid. På vissa språk görs detta med ett nyckelord (t.ex. const i C++ , final i Java ) som betecknar fältet som oföränderligt. Vissa språk vänder på det: i OKaml är fält för ett objekt eller post som standard oföränderliga och måste uttryckligen markeras med mutable för att vara det.

Referenser till föremål

I de flesta objektorienterade språk kan objekt hänvisas till med hjälp av referenser . Några exempel på sådana språk är Java , C++ , C# , VB.NET och många skriptspråk , som Perl , Python och Ruby . I det här fallet spelar det roll om ett objekts tillstånd kan variera när objekt delas via referenser.

Referera vs kopiering av objekt

Om ett objekt är känt för att vara oföränderligt, är det att föredra att skapa en referens till det istället för att kopiera hela objektet. Detta görs för att spara minne genom att förhindra dataduplicering och undvika anrop till konstruktörer och destruktörer; det resulterar också i en potentiell ökning av exekveringshastigheten.

Referenskopieringstekniken är mycket svårare att använda för föränderliga objekt, för om någon användare av en föränderlig objektreferens ändrar den, ser alla andra användare av den referensen ändringen. Om detta inte är den avsedda effekten kan det vara svårt att meddela de andra användarna att de ska svara korrekt. I dessa situationer defensiv kopiering av hela objektet snarare än referensen vanligtvis en enkel men kostsam lösning. Observatörsmönstret är en alternativ teknik för att hantera förändringar av föränderliga objekt .

Kopiera-på-skriva

En teknik som blandar fördelarna med föränderliga och oföränderliga objekt, och som stöds direkt i nästan all modern hårdvara, är copy-on-write (COW). Med denna teknik, när en användare ber systemet att kopiera ett objekt, skapar det istället bara en ny referens som fortfarande pekar på samma objekt. Så snart en användare försöker modifiera objektet genom en viss referens, gör systemet en riktig kopia, tillämpar modifieringen på det och ställer in referensen att referera till den nya kopian. De andra användarna påverkas inte, eftersom de fortfarande hänvisar till det ursprungliga objektet. Därför, under COW, verkar alla användare ha en föränderlig version av sina objekt, även om de utrymmesbesparande och snabba fördelarna med oföränderliga objekt bevaras om användarna inte ändrar sina objekt. Kopiera-på-skriv är populärt i virtuella minnessystem eftersom det tillåter dem att spara minnesutrymme samtidigt som de fortfarande hanterar allt som ett applikationsprogram kan göra på rätt sätt.

Praktikant

Bruket att alltid använda referenser i stället för kopior av lika objekt kallas internering . Om internering används anses två objekt vara lika om och endast om deras referenser, vanligtvis representerade som pekare eller heltal, är lika. Vissa språk gör detta automatiskt: till exempel Python automatiskt in korta strängar . Om algoritmen som implementerar internering garanterat gör det i alla fall som det är möjligt, reduceras jämförande av objekt för jämlikhet till att jämföra deras pekare – en avsevärd hastighetsvinst i de flesta applikationer. (Även om algoritmen inte garanteras att vara heltäckande, finns det fortfarande möjlighet till en snabb förbättring av sökvägsfall när objekten är lika och använder samma referens.) Internering är i allmänhet bara användbart för oföränderliga objekt.

Trådsäkerhet

Oföränderliga objekt kan vara användbara i flertrådade applikationer. Flera trådar kan agera på data som representeras av oföränderliga objekt utan oro för att data ändras av andra trådar. Oföränderliga objekt anses därför vara mer trådsäkra än föränderliga objekt.

Kränker oföränderligheten

Oföränderlighet innebär inte att objektet som lagrats i datorns minne är oskrivbart. Snarare är oföränderlighet en kompileringstidskonstruktion som indikerar vad en programmerare kan göra genom objektets normala gränssnitt, inte nödvändigtvis vad de absolut kan göra (till exempel genom att kringgå typsystemet eller bryta mot const correctness i C eller C++ ).

Språkspecifika detaljer

I Python , Java och .NET Framework är strängar oföränderliga objekt. Både Java och .NET Framework har föränderliga versioner av sträng. I Java är dessa StringBuffer och StringBuilder (föränderliga versioner av Java String ) och i .NET är detta StringBuilder (föränderlig version av .Net String ). Python 3 har en föränderlig strängvariant (bytes), som heter bytearray .

Dessutom är alla de primitiva omslagsklasserna i Java oföränderliga.

Liknande mönster är Immutable Interface och Immutable Wrapper.

I rena funktionella programmeringsspråk är det inte möjligt att skapa föränderliga objekt utan att utöka språket (t.ex. via ett föränderligt referensbibliotek eller ett främmande funktionsgränssnitt ), så alla objekt är oföränderliga.

Ada

I Ada deklareras vilket objekt som helst som antingen variabel (dvs. föränderlig; vanligtvis den implicita standarden), eller konstant (dvs. oföränderlig) via nyckelordet konstant .

       
      
     typ  Some_type  är  nytt  heltal  ;  -- kan vara något mer komplicerat  x  :  konstant  Some_type  :=  1  ;  -- oföränderlig  y  :  Some_type  ;  -- föränderlig 

Underprogramparametrar är oföränderliga i in- läge och föränderliga i in-ut- och ut -läge.

          
  
    
       
     
    proceduren  Do_it  (  a  :  i  heltal  ;  b  :  in  ut  heltal  ;  c  :  ut  heltal  )  börjar  - a är oföränderlig  b  :  =  b  +  a  ;  c  :=  a  ;  slut  Gör_det  ; 

C#

I C# kan du framtvinga oföränderlighet av fälten i en klass med skrivskyddat uttalande. Genom att upprätthålla alla fält som oföränderliga får du en oföränderlig typ.

 

       
       
     
           
    
       
     
             
    
 class  AnImmutableType  {  public  readonly  double  _value  ;  public  AnImmutableType  (  double  x  )  {  _value  =  x  ;  }  public  AnImmutableType  Square  ()  {  returnera  ny  AnImmutableType  (  _value  *  _value  );  }  } 

C++

I C++ skulle en const-korrekt implementering av Cart tillåta användaren att skapa instanser av klassen och sedan använda dem som antingen const (oföränderliga) eller föränderliga, efter önskemål, genom att tillhandahålla två olika versioner av metoden items() . (Observera att i C++ är det inte nödvändigt – och faktiskt omöjligt – att tillhandahålla en specialiserad konstruktör för konstinstanser .)

  
 
     

       
         

       

 
   
 class  Cart  {  public  :  Cart  (  std  ::  vektor  <  Artikel  >  objekt  )  :  items_  (  objekt  )  {}  std  ::  vektor  <  Artikel  > &  objekt  ( )  {  return  items_  ;  }  const  std  ::  vektor  <  Item  >&  items  ()  const  {  return  items_  ;  }  int  ComputeTotalCost  ()  const  {  /* return summan av priserna */  }  private  :  std  ::  vector  <  Item  >  items_  ;  }; 

Observera att när det finns en datamedlem som är en pekare eller referens till ett annat objekt, så är det möjligt att mutera objektet som pekas på eller refereras till endast inom en icke-konst-metod.

C++ ger också abstrakt (i motsats till bitvis) oföränderlighet via det mutable nyckelordet, som låter en medlemsvariabel ändras inifrån en const- metod.

  
 
     

         

     
      
       
    

       0
          
        
    
      
     
  

 
   
    
 klass  Varukorg  {  public  :  Varukorg  (  std  ::  vektor  <  Item  >  items  )  :  items_  (  items  )  {}  const  std  ::  vektor  <  Item  >&  items  ()  const  {  return  items_  ;  }  int  ComputeTotalCost  ()  const  {  if  (  total_cost_  )  {  return  *  total_cost_  ;  }  int  total_cost  =  ;  för  (  const  auto  &  item  :  items_  )  {  total_cost  +=  item  .  Kostnad  ();  }  total_cost_  =  total_cost  ;  returnera  total_kostnad  ;  }  privat  :  std  ::  vektor  <  Objekt  >  items_  ;  mutable  std  ::  valfritt  <  int  >  total_cost_  ;  }; 

D

I D finns det två typkvalificerare , const och immutable , för variabler som inte kan ändras. Till skillnad från C++'s const , Java's final och C#'s readonly , är de transitiva och tillämpas rekursivt på allt som kan nås genom referenser till en sådan variabel. Skillnaden mellan const och oföränderlig är vad de gäller: const är en egenskap hos variabeln: det kan lagligen existera föränderliga referenser till refererat värde, dvs värdet kan faktiskt ändras. Däremot oföränderlig en egenskap hos det refererade värdet: värdet och allt som transitivt kan nås från det kan inte ändras (utan att bryta typsystemet, vilket leder till odefinierat beteende ). Alla referenser till det värdet måste markeras som const eller oföränderliga . I grund och botten för varje okvalificerad typ T är const(T) den disjunkta föreningen av T (föränderlig) och oföränderlig(T ) .

  
    
          
      
 class  C  {  /*mutable*/  Object  mField  ;  const  Objekt  cField  ;  oföränderligt  objekt  iField  ;  } 

För ett föränderligt C -objekt kan dess mField skrivas till. För ett const(C) -objekt kan mField inte ändras, det ärver const ; iField är fortfarande oföränderligt eftersom det är den starkare garantin. För en oföränderlig(C) är alla fält oföränderliga.

I en funktion som denna:

        
   void  func  (  C  m  ,  const  C  c  ,  oföränderlig  C  i  )  {  /* innanför klammerparenteserna */  } 

Inuti hängslen kan c referera till samma objekt som m , så mutationer till m kan indirekt också ändra c . Dessutom kan c referera till samma objekt som i , men eftersom värdet då är oföränderligt, finns det inga ändringar. Men m och i kan inte juridiskt referera till samma objekt.

På garantispråket har mutable inga garantier (funktionen kan ändra objektet), const är en utåtriktad garanti för att funktionen inte kommer att ändra någonting, och immutable är en dubbelriktad garanti (funktionen kommer inte att ändra värdet och den som ringer får inte ändra det).

Värden som är konstanta eller oföränderliga måste initieras genom direkt tilldelning vid deklarationspunkten eller av en konstruktor .

Eftersom const- parametrar glömmer om värdet var föränderligt eller inte, fungerar en liknande konstruktion, inout , på sätt och vis som en variabel för information om förändringar. En funktion av typen const(S) function(const(T)) returnerar const(S) -skrivna värden för föränderliga, const och oföränderliga argument. Däremot returnerar en funktion av typen inout(S) function(inout(T)) S för föränderliga T- argument, const(S) för const(T) -värden och oföränderlig(S) för oföränderliga(T) -värden.

Att kasta oföränderliga värden till föränderliga orsakar odefinierat beteende vid förändring, även om det ursprungliga värdet kommer från ett föränderligt ursprung. Att casta föränderliga värden till oföränderliga kan vara lagligt när det inte finns några föränderliga referenser efteråt. "Ett uttryck kan konverteras från föränderligt (...) till oföränderligt om uttrycket är unikt och alla uttryck det transitivt refererar till är antingen unika eller oföränderliga." Om kompilatorn inte kan bevisa unikhet kan castingen göras explicit och det är upp till programmeraren att se till att inga föränderliga referenser finns.

Typsträngen är ett alias för immutable(char)[] , dvs en maskinskriven minnesdel av oföränderliga tecken . Att göra delsträngar är billigt, eftersom det bara kopierar och modifierar en pekare och en längd som är arkiverad, och säker, eftersom de underliggande data inte kan ändras. Objekt av typen const(char)[] kan referera till strängar, men också till muterbara buffertar.

Att göra en ytlig kopia av ett const eller oföränderligt värde tar bort det yttre lagret av oföränderlighet: Att kopiera en oföränderlig sträng ( immutable(char[]) ) returnerar en sträng ( immutable(char)[] ). Den oföränderliga pekaren och längden kopieras och kopiorna är föränderliga. Den refererade datan har inte kopierats och behåller sin kvalificering, i exemplet oföränderlig . Den kan tas bort genom att göra en depper kopia, t.ex. med dup -funktionen.

Java

Ett klassiskt exempel på ett oföränderligt objekt är en instans av Java String -klassen

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

Metoden toLowerCase() ändrar inte data "ABC" som s innehåller. Istället instansieras ett nytt String-objekt och ges data "abc" under dess konstruktion. En referens till det här String-objektet returneras av metoden toLowerCase() . För att få strängarna att innehålla data "abc" behövs ett annat tillvägagångssätt:

   s  =  s  .  toLowerCase  (); 

Nu refererar String s till ett nytt String-objekt som innehåller "abc". Det finns inget i syntaxen för deklarationen av klassen String som framtvingar den som oföränderlig; snarare, ingen av String-klassens metoder påverkar någonsin data som ett String-objekt innehåller, vilket gör det oföränderligt.

Nyckelordet final ( detaljerad artikel ) används för att implementera oföränderliga primitiva typer och objektreferenser, men det kan inte i sig göra själva objekten oföränderliga. Se nedanstående exempel:

Primitiva typvariabler ( int , long , short , etc.) kan tilldelas om efter att ha definierats. Detta kan förhindras genom att använda final .

    
   

    
    int  i  =  42  ;  //int är en primitiv typ  i  =  43  ;  // OK  final  int  j  =  42  ;  j  =  43  ;  // kompilerar inte. j är slutgiltigt så kan inte tilldelas om  

Referenstyper kan inte göras oföränderliga bara genom att använda det sista nyckelordet. final förhindrar endast omplacering.

      
   
     final  MyObject  m  =  new  MyObject  ();  //m är av referenstyp  m  .  data  =  100  ;  // OK. Vi kan ändra tillståndet för objektet m (m är föränderligt och final ändrar inte detta faktum)   m  =  new  MyObject  ();  // kompilerar inte. m är slutgiltig så kan inte tilldelas om  

Primitiva omslag ( heltal , långt , kort , dubbelt , flytande , tecken , byte , booleskt ) är också alla oföränderliga. Oföränderliga klasser kan implementeras genom att följa några enkla riktlinjer.

JavaScript

I JavaScript är alla primitiva typer (Odefinierad, Null, Boolean, Number, BigInt, String, Symbol) oföränderliga, men anpassade objekt är i allmänhet föränderliga.

    
   
      
         
         
   function  doSomething  (  x  )  {  /* ändras originalet om du ändrar x här? */   };  var  str  =  'en sträng'  ;  var  obj  =  {  an  :  'objekt'  };  göra Något  (  str  );  // strängar, siffror och booltyper är oföränderliga, funktion får en kopia  doSomething  (  obj  );  // objekt skickas in genom referens och är föränderliga inuti funktionen  doAnotherThing  (  str  ,  obj  );  // `str` har inte ändrats, men `obj` kan ha ändrats. 

För att simulera oföränderlighet i ett objekt kan man definiera egenskaper som skrivskyddade (skrivbar: falsk).

   
       
    var  obj  =  {};  Objekt  .  defineProperty  (  obj  ,  'foo'  ,  {  värde  :  'bar'  ,  skrivbar  :  false  });  obj  .  foo  =  'bar2'  ;  // ignoreras tyst 

Men tillvägagångssättet ovan låter fortfarande nya fastigheter läggas till. Alternativt kan man använda Object.freeze för att göra befintliga objekt oföränderliga.

      

   
    var  obj  =  {  foo  :  'bar'  };  Objekt  .  frysa  (  obj  );  obj  .  foo  =  'stänger'  ;  // kan inte redigera egenskapen, ignoreras tyst  obj  .  foo2  =  'bar2'  ;  // kan inte lägga till egenskap, ignoreras tyst 

Med implementeringen av ECMA262 har JavaScript möjlighet att skapa oföränderliga referenser som inte kan tilldelas om. Att använda en const- deklaration betyder dock inte att värdet på den skrivskyddade referensen är oföränderligt, bara att namnet inte kan tilldelas ett nytt värde.

   

 
    
   
  


     

  const  ALWAYS_IMMUTABLE  =  sant  ;  försök  {  ALWAYS_IMMUTABLE  =  false  ;  }  fånga  (  fela  )  {  konsol  .  log  (  "Kan inte omtilldela en oföränderlig referens." )  ;  }  const  arr  =  [  1  ,  2  ,  3  ];  arr  .  tryck  (  4  );  konsol  .  log  (  arr  );  // [1, 2, 3, 4] 

Användningen av oföränderligt tillstånd har blivit en stigande trend i JavaScript sedan introduktionen av React , som gynnar Flux-liknande tillståndshanteringsmönster som Redux .

Perl

I Perl kan man skapa en oföränderlig klass med Moo-biblioteket genom att helt enkelt förklara alla attribut som läsbara:

 
 

   
              
       
                       


 paket  Immutable  ;  använd  Moo  ;  har  värde  =>  (  är  =>  'ro'  ,  # skrivskyddad  standard  =>  'data'  ,  # kan åsidosättas genom att förse konstruktorn med  # ett värde: Immutable->new(value => 'något annat');  ) ;  1  ; 

Att skapa en oföränderlig klass brukade kräva två steg: för det första skapa accessorer (antingen automatiskt eller manuellt) som förhindrar modifiering av objektattribut, och för det andra förhindrar direkt modifiering av instansdata för instanser av den klassen (detta lagrades vanligtvis i en hash referens, och kan låsas med Hash::Utils lock_hash-funktion):

 
 
 
  


  

  
       
       
     
             0
       
          
    
       
        
        
    
      
    
     

 paket  Immutable  ;  använd  strikt  ;  använd  varningar  ;  använd  basen  qw(Class::Accessor)  ;  # skapa skrivskyddade accessorer  __PACKAGE__  ->  mk_ro_accessors  (  qw(värde) )  ;  använd  Hash::Util  'lock_hash'  ;  sub  new  {  min  $klass  =  skift  ;  returnera  $klass  om  ref  (  $klass  );  die  "Argument till nya måste vara nyckel => värdepar\n"  om inte  (  @_  %  2  ==  );  mina  %defaults  =  (  värde  =>  'data'  ,  );  min  $obj  =  {  %defaults  ,  @_  ,  };  välsigna  $obj  ,  $klass  ;  # förhindra modifiering av objektdata  lock_hash  %$obj  ;  }  1  ; 

Eller, med en manuellt skriven accessor:

 
 
 
  

  
       
       
     
             0
       
          
    
       
        
        
    
      
    
     



  
       
         
        
         
      
         
    

 paket  Immutable  ;  använd  strikt  ;  använd  varningar  ;  använd  Hash::Util  'lock_hash'  ;  sub  new  {  min  $klass  =  skift  ;  returnera  $klass  om  ref  (  $klass  );  die  "Argument till nya måste vara nyckel => värdepar\n"  om inte  (  @_  %  2  ==  );  mina  %defaults  =  (  värde  =>  'data'  ,  );  min  $obj  =  {  %defaults  ,  @_  ,  };  välsigna  $obj  ,  $klass  ;  # förhindra modifiering av objektdata  lock_hash  %$obj  ;  }  # skrivskyddad accessor  subvärde  {  my  $  self  =  shift  ;  if  (  my  $new_value  =  shift  )  {  # försöker sätta ett nytt värde  tärning  "Detta objekt kan inte ändras\n"  ;  }  else  {  return  $self  ->  {  value  }  }  }  1  ; 

Pytonorm

I Python är vissa inbyggda typer (siffror, booleans, strängar, tupler, frysta set) oföränderliga, men anpassade klasser är i allmänhet föränderliga. För att simulera oföränderlighet i en klass, kan man åsidosätta attributinställning och radering för att skapa undantag:

 
    

       

      
         

      

       
        
        
         
          class  ImmutablePoint  :  """En oföränderlig klass med två attribut 'x' och 'y'."""  __slots__  =  [  'x'  ,  'y'  ]  def  __setattr__  (  self  ,  *  args  ):  raise  TypeError  (  "Kan inte ändras oföränderlig instans."  )  __delattr__  =  __setattr__  def  __init__  (  själv  ,  x  ,  y  ):  # Vi kan inte längre använda self.value = värde för att lagra instansdata #  så vi måste uttryckligen kalla superklassen  super  ()  .  __setattr__  (  'x'  ,  x  )  super  ()  .  __setattr__  (  'y'  ,  y  ) 

Standardbibliotekshjälparna collections.namedtuple och typing.NamedTuple , tillgängliga från Python 3.6 och framåt, skapar enkla oföränderliga klasser. Följande exempel är ungefär lika med ovanstående, plus några tuppelliknande funktioner:

   
 

    


 
     
      från  att skriva  import  NamedTuple  import  collections  Point  =  samlingar  .  namedtuple  (  'Point'  ,  [  'x'  ,  'y'  ])  # följande skapar en liknande namedtuple till ovanstående  klass  Point  (  NamedTuple  ):  x  :  int  y  :  int 

Dataklasser introducerades i Python 3.7 och tillåter utvecklare att emulera oföränderlighet med frusna instanser . Om en fryst dataklass byggs dataklasser att åsidosätta __setattr__() och __delattr__() för att öka FrozenInstanceError om de anropas.

   


 
     
      från  dataklasser  importera  dataklass  @dataclass  (  fryst  =  True  )  klass  Punkt  :  x  :  int  y  :  int 

Racket

Racket avviker väsentligt från andra Scheme- implementeringar genom att göra dess kärnpartyp ("cons celler") oföränderlig. Istället ger den en parallell föränderlig partyp, via mcons , mcar , set-mcar! etc. Dessutom stöds många oföränderliga typer, till exempel oföränderliga strängar och vektorer, och dessa används flitigt. Nya strukturer är oföränderliga som standard, om inte ett fält specifikt förklaras föränderligt, eller hela strukturen:

                
     
        (  struktur  foo1  (  x  y  ))  ; alla fält oföränderliga   (  struct  foo2  (  x  [  y  #: föränderlig ])  )  ; ett föränderligt fält   (  struct  foo3  (  x  y  )  #: föränderligt  )  ; alla fält är föränderliga  

Språket stöder också oföränderliga hashtabeller, implementerade funktionellt och oföränderliga ordböcker.

Rost

Rusts ägarsystem tillåter utvecklare att deklarera oföränderliga variabler och skicka oföränderliga referenser. Som standard är alla variabler och referenser oföränderliga. Föränderliga variabler och referenser skapas uttryckligen med nyckelordet mut .

Konstanta objekt i Rust är alltid oföränderliga.


   

  
    
    


  
    
            
       

        
       

       
       

    
           
       

       
          

       
       
    
 // konstanta objekt är alltid oföränderliga  const  ALWAYS_IMMUTABLE  :  bool  =  true  ;  struct  Object  {  x  :  usize  ,  y  :  usize  ,  }  fn  main  ( )  {  // deklarera explicit en föränderlig variabel  let  mut  mutable_obj  =  Object  {  x  :  1  ,  y  :  2  };  mutable_obj  .  x  =  3  ;  // okej  låt  mutable_ref  =  &  mut  mutable_obj  ;  mutable_ref  .  x  =  1  ;  // okej  låt  immutable_ref  =  &  mutable_obj  ;  immutable_ref  .  x  =  3  ;  // fel E0594  // som standard, variabler är oföränderliga  let  immutable_obj  =  Objekt  {  x  :  4  ,  y  :  5  };  immutable_obj  .  x  =  6  ;  // error E0596  let  mutable_ref2  =  &  mut  immutable_obj  ;  // error E0596  let  immutable_ref2  =  &  immutable_obj  ;  oföränderlig_ref2  .  x  =  6  ;  // fel E0594  } 

Scala

I Scala kan vilken entitet som helst (snävt, en bindning) definieras som föränderlig eller oföränderlig: i deklarationen kan man använda val (värde) för oföränderliga entiteter och var (variabel) för föränderliga. Observera att även om en oföränderlig bindning inte kan tilldelas om, kan den fortfarande hänvisa till ett föränderligt objekt och det är fortfarande möjligt att anropa muterande metoder för det objektet: bindningen är oföränderlig , men det underliggande objektet kan vara föränderligt.

Till exempel följande kodavsnitt:

   
    val  maxValue  =  100  var  currentValue  =  1 

definierar en oföränderlig entitet maxValue (heltalstypen antas vid kompilering) och en föränderlig entitet som heter currentValue .

Som standard är samlingsklasser som List och Map oföränderliga, så uppdateringsmetoder returnerar en ny instans istället för att mutera en befintlig. Även om detta kan låta ineffektivt, innebär implementeringen av dessa klasser och deras garantier för oföränderlighet att den nya instansen kan återanvända befintliga noder, vilket, särskilt när det gäller att skapa kopior, är mycket effektivt. [ bättre källa behövs ]

Se även

Den här artikeln innehåller en del material från Perl Design Patterns Book

externa länkar