stringtranslate.com

Неизменяемый объект

В объектно-ориентированном (ОО) и функциональном программировании неизменяемый объект (неизменяемый [1] объект) — это объект , состояние которого не может быть изменено после его создания. [2] В этом отличие от изменяемого объекта (изменяемого объекта), который можно изменить после его создания. [3] В некоторых случаях объект считается неизменным, даже если изменяются некоторые внутренние атрибуты, но состояние объекта кажется неизменным с внешней точки зрения. Например, объект, который использует мемоизацию для кэширования результатов дорогостоящих вычислений, по-прежнему может считаться неизменяемым объектом.

Строки и другие конкретные объекты обычно выражаются как неизменяемые объекты для улучшения читаемости и эффективности выполнения объектно-ориентированного программирования. Неизменяемые объекты также полезны, поскольку они по своей сути потокобезопасны . [2] Другие преимущества заключаются в том, что их проще понять и обосновать, а также они обеспечивают более высокий уровень безопасности, чем изменяемые объекты. [2]

Концепции

Неизменяемые переменные

В императивном программировании значения, хранящиеся в переменных программы , содержимое которых никогда не меняется, называются константами , чтобы отличать их от переменных, которые могут быть изменены во время выполнения. Примеры включают коэффициенты перевода метров в футы или значение числа Пи с точностью до нескольких десятичных знаков.

Поля, доступные только для чтения, могут рассчитываться во время работы программы (в отличие от констант, которые известны заранее), но никогда не изменяются после их инициализации.

Слабая и сильная неизменяемость

Иногда говорят о неизменяемости определенных полей объекта. Это означает, что невозможно изменить эти части состояния объекта, даже если другие части объекта могут быть изменяемыми ( слабо неизменяемыми ). Если все поля неизменяемы, то объект является неизменяемым. Если весь объект не может быть расширен другим классом, объект называется строго неизменяемым . [4] Это может, например, помочь явно обеспечить соблюдение определенных инвариантов в отношении определенных данных в объекте, которые останутся неизменными на протяжении всего срока службы объекта. В некоторых языках это делается с помощью ключевого слова (например, constв C++ , finalв Java ), которое обозначает поле как неизменяемое. В некоторых языках все наоборот: в OCaml поля объекта или записи по умолчанию являются неизменяемыми и mutableдля этого должны быть явно помечены значком.

Ссылки на объекты

В большинстве объектно-ориентированных языков на объекты можно ссылаться с помощью ссылок . Некоторыми примерами таких языков являются Java , C++ , C# , VB.NET и многие языки сценариев , такие как Perl , Python и Ruby . В этом случае имеет значение, может ли состояние объекта меняться при совместном использовании объектов через ссылки.

Ссылки и копирование объектов

Если известно, что объект является неизменяемым, предпочтительно создать ссылку на него, а не копировать весь объект. Это сделано для экономии памяти за счет предотвращения дублирования данных и избежания вызовов конструкторов и деструкторов; это также приводит к потенциальному увеличению скорости выполнения.

Технику копирования ссылок гораздо сложнее использовать для изменяемых объектов, поскольку если какой-либо пользователь ссылки на изменяемый объект меняет ее, все остальные пользователи этой ссылки увидят это изменение. Если это не ожидаемый эффект, может быть сложно уведомить других пользователей о том, чтобы они отреагировали правильно. В таких ситуациях защитное копирование всего объекта, а не ссылки, обычно является простым, но дорогостоящим решением. Шаблон наблюдателя — это альтернативный метод обработки изменений изменяемых объектов.

Копирование при записи

Метод копирования при записи (COW) сочетает в себе преимущества изменяемых и неизменяемых объектов и напрямую поддерживается практически всем современным оборудованием . Используя этот метод, когда пользователь просит систему скопировать объект, она вместо этого просто создает новую ссылку, которая по-прежнему указывает на тот же объект. Как только пользователь пытается изменить объект с помощью определенной ссылки, система создает реальную копию, применяет к ней изменения и устанавливает ссылку для ссылки на новую копию. На других пользователей это не влияет, поскольку они по-прежнему ссылаются на исходный объект. Таким образом, в COW все пользователи имеют изменяемую версию своих объектов, хотя в случае, если пользователи не изменяют свои объекты, преимущества неизменяемых объектов в экономии места и скорости сохраняются. Копирование при записи популярно в системах виртуальной памяти, поскольку позволяет им экономить пространство памяти, сохраняя при этом правильную обработку всего, что может делать прикладная программа.

Стажировка

Практика постоянного использования ссылок вместо копий одинаковых объектов называется интернированием . Если используется интернирование, два объекта считаются равными тогда и только тогда, когда их ссылки, обычно представленные в виде указателей или целых чисел, равны. Некоторые языки делают это автоматически: например, Python автоматически интернирует короткие строки . Если алгоритм, реализующий интернирование, гарантированно делает это во всех возможных случаях, то сравнение объектов на равенство сводится к сравнению их указателей — существенный выигрыш в скорости в большинстве приложений. (Даже если не гарантируется полнота алгоритма, все равно существует возможность улучшения быстрого пути , когда объекты равны и используют одну и ту же ссылку.) Интернирование обычно полезно только для неизменяемых объектов.

Безопасность резьбы

Неизменяемые объекты могут быть полезны в многопоточных приложениях. Несколько потоков могут работать с данными, представленными неизменяемыми объектами, не беспокоясь об изменении данных другими потоками. Поэтому неизменяемые объекты считаются более потокобезопасными , чем изменяемые объекты.

Нарушение неизменности

Неизменяемость не означает, что объект, хранящийся в памяти компьютера, не подлежит записи. Скорее, неизменяемость — это конструкция времени компиляции , которая указывает, что программист может сделать через обычный интерфейс объекта, а не обязательно то, что он может сделать абсолютно (например, обходя систему типов или нарушая корректность const в C или C++ ).

Подробности, относящиеся к языку

В Python , Java [5] :80  и .NET Framework строки являются неизменяемыми объектами. И в Java, и в .NET Framework есть изменяемые версии строк. В Java [5] : 84  это StringBufferи StringBuilder(изменяемые версии Java String), а в .NET это StringBuilder(изменяемая версия .Net String). В Python 3 есть вариант изменяемой строки (байтов) с именем bytearray. [6]

Кроме того, все примитивные классы-оболочки в Java являются неизменяемыми.

Аналогичными шаблонами являются неизменяемый интерфейс и неизменяемая оболочка.

В чисто функциональных языках программирования невозможно создавать изменяемые объекты без расширения языка (например, с помощью библиотеки изменяемых ссылок или интерфейса внешних функций ), поэтому все объекты являются неизменяемыми.

Ада

В Ada любой объект объявляется либо переменным (т. е. изменяемым; обычно это неявное значение по умолчанию), либо constant(т. е. неизменяемым) с помощью constantключевого слова.

 введите  Some_type   новое  целое число ;  -- может быть что-нибудь более сложное  x :  constant  Some_type :=  1 ;  -- неизменяемый  y :  Some_type ;  -- изменчивый

Параметры подпрограммы неизменяемы в режиме in и изменяемы в режимах in out и out .

 процедура  Do_it ( a : in  Integer ;  b : in  out  Integer ;  c : out  Integer )  is  Begin  -- a неизменяема  b :=  b  +  a ;  с :=  а ;  закончить  Do_it ;

С#

В C# вы можете обеспечить неизменность полей класса с помощью этого readonlyоператора. [7] : 239  Сделав все поля неизменяемыми, вы получите неизменяемый тип.

класс AnImmutableType { public readonly double _value ; общественный AnImmutableType ( двойной х ) { _value = х ; } Общественный AnImmutableType Square () { вернуть новый AnImmutableType ( _value * _value ); } }                             

С++

В C++ константно-корректная реализация Cartпозволит пользователю создавать экземпляры класса, а затем использовать их либо как const(неизменяемые), либо как изменяемые, по желанию, предоставляя две разные версии метода items(). (Обратите внимание, что в C++ нет необходимости (и фактически невозможно) предоставлять специализированный конструктор для constэкземпляров.)

class Cart { public : Cart ( std :: вектор < Item > items ) : items_ ( items ) {}        std :: vector < Item >& items () { return items_ ; } const std :: vector < Item >& items () const { return items_ ; }              int ComputeTotalCost () const { /* возвращаем сумму цен */ }      частный : std :: vector <Item> items_ ; _ _ };  

Обратите внимание: если существует элемент данных, который является указателем или ссылкой на другой объект, то изменить объект, на который указывает или на который ссылается ссылка, можно только внутри неконстантного метода.

C++ также обеспечивает абстрактную (в отличие от побитовой) неизменяемость посредством mutableключевого слова, которое позволяет изменять переменную-член изнутри constметода.

class Cart { public : Cart ( std :: вектор < Item > items ) : items_ ( items ) {}        const std :: вектор < Item >& items () const { return items_ ; }        int ComputeTotalCost () const { if ( tal_cost_ ) { return * total_cost_ ; }          int total_cost = 0 ; for ( const auto & item : items_ ) { tal_cost += item . Расходы (); } Total_cost_ = total_cost ; вернуть общую_стоимость ; }                     частный : std :: vector <Item> items_ ; _ _ изменяемый std :: необязательный <int> total_cost_ ; _ _ };     

Д

В D существуют два квалификатора типа и для переменных const, immutableкоторые нельзя изменить. [8] В отличие от C++ const, Java finalи C# readonly, они транзитивны и рекурсивно применяются ко всему, что достижимо через ссылки на такую ​​переменную. Разница между constи immutableзаключается в том, к чему они применяются: constэто свойство переменной: на законном основании могут существовать изменяемые ссылки на указанное значение, т. е. значение может фактически измениться. Напротив, immutableявляется свойством указанного значения: значение и все, что транзитивно достижимо из него, не может измениться (без нарушения системы типов, что приводит к неопределенному поведению ). Любая ссылка на это значение должна быть помечена constили immutable. TПо сути , для любого неквалифицированного типа const(T)это непересекающееся объединение T(изменяемого) и immutable(T).

класс C { /*mutable*/ Object mField ; константный объект cField ; неизменяемый объект iField ; }           

Для изменяемого Cобъекта в него mFieldможно записать. Объект не может быть изменен , он наследует const(C); по-прежнему неизменен, поскольку это более сильная гарантия. Для a все поля неизменяемы.mFieldconstiFieldimmutable(C)

В такой функции:

void func ( C m , const C c , неизменяемый C i ) { /* внутри фигурных скобок */ }          

Внутри фигурных скобок cможет ссылаться на тот же объект, что и m, поэтому мутации на также mмогут косвенно меняться . cКроме того, cможет ссылаться на тот же объект, что и i, но, поскольку значение тогда является неизменным, изменений не происходит. Однако mи iне может юридически относиться к одному и тому же объекту.

На языке гарантий mutable не имеет никаких гарантий (функция может изменить объект), constявляется только внешней гарантией того, что функция ничего не изменит, и immutableявляется двунаправленной гарантией (функция не изменит значение, и вызывающая сторона должна не менять).

Значения, которые инициализируются constили immutableдолжны быть инициализированы путем прямого присвоения в точке объявления или с помощью конструктора .

Поскольку constпараметры забывают, было ли значение изменяемым или нет, подобная конструкция inoutдействует в некотором смысле как переменная для информации об изменяемости. Функция типа const(S) function(const(T))возвращает const(S)типизированные значения для изменяемых, константных и неизменяемых аргументов. Напротив, функция типа inout(S) function(inout(T))возвращает значение Sдля изменяемых Tаргументов, const(S)значений const(T)и immutable(S)значений immutable(T).

Приведение неизменяемых значений к изменяемым приводит к неопределенному поведению при изменении, даже если исходное значение происходит из изменяемого источника. Приведение изменяемых значений к неизменяемым может быть законным, если после этого не останется изменяемых ссылок. «Выражение может быть преобразовано из изменяемого (...) в неизменяемое, если выражение уникально и все выражения, на которые оно транзитивно ссылается, либо уникальны, либо неизменяемы». [8] Если компилятор не может доказать уникальность, приведение может быть выполнено явно, и программист должен гарантировать отсутствие изменяемых ссылок.

Тип stringявляется псевдонимом для immutable(char)[], то есть типизированным фрагментом памяти, состоящим из неизменяемых символов. [9] Создание подстрок обходится дешево, поскольку просто копирует и изменяет указатель и поле длины, и безопасно, поскольку базовые данные не могут быть изменены. Объекты типа const(char)[]могут ссылаться на строки, а также на изменяемые буферы.

Создание поверхностной копии константного или неизменяемого значения удаляет внешний слой неизменяемости: копирование неизменяемой строки ( immutable(char[])) возвращает строку ( immutable(char)[]). Неизменяемый указатель и длина копируются, а копии изменяемы. Указанные данные не были скопированы и сохраняют свой квалификатор, как в примере immutable. Его можно удалить, создав глубокую копию, например, с помощью dupфункции.

Джава

Классическим примером неизменяемого объекта является экземпляр класса Java String.

Строка s = "ABC" ; с . в нижний регистр (); // Это ничего не дает!    

Метод toLowerCase()не изменяет содержащиеся в нем данные «ABC» s. Вместо этого создается экземпляр нового объекта String, которому во время создания присваиваются данные «abc». Ссылка на этот объект String возвращается методом toLowerCase(). Чтобы строка sсодержала данные «abc», необходим другой подход:

с = с . в нижний регистр ();  

Теперь String sссылается на новый объект String, содержащий «abc». В синтаксисе объявления класса String нет ничего , что делало бы его неизменяемым; скорее, ни один из методов класса String никогда не влияет на данные, содержащиеся в объекте String, что делает его неизменяемым.

Ключевое слово final( подробная статья ) используется при реализации неизменяемых примитивных типов и ссылок на объекты, [10] но само по себе оно не может сделать сами объекты неизменяемыми. См. примеры ниже:

Переменные примитивного типа ( int, long, shortи т. д.) можно переназначать после определения. Этого можно избежать, используя final.

интервал я = 42 ; //int — примитивный тип i = 43 ; // ХОРОШО       окончательный int j = 42 ; j = 43 ; // не компилируется. j является окончательным, поэтому его нельзя переназначить       

Ссылочные типы нельзя сделать неизменяемыми, просто используя finalключевое слово. finalтолько предотвращает переназначение.

окончательный MyObject м = новый MyObject (); //m имеет ссылочный тип m . данные = 100 ; // ХОРОШО. Мы можем изменить состояние объекта m (m изменчив, и Final не меняет этого факта) m = new MyObject (); // не компилируется. m является окончательным, поэтому его нельзя переназначить             

Примитивные оболочки ( Integer, Long, Short, Double, Float, Character, Byte, Boolean) также являются неизменяемыми. Неизменяемые классы можно реализовать, следуя нескольким простым рекомендациям. [11]

JavaScript

В JavaScript все примитивные типы (Undefine, Null, Boolean, Number, BigInt, String, Symbol) являются неизменяемыми, но пользовательские объекты обычно изменяемы.

function doSomething ( x ) { /* изменение x здесь меняет оригинал? */ }; вар стр = 'строка' ; вар объект = { ан : 'объект' }; сделать что-нибудь ( ул ); // строки, числа и типы bool неизменяемы, функция получает копию doSomething ( obj ); // объекты передаются по ссылке и изменяются внутри функции doAnotherThing ( str , obj ); // `str` не изменился, но `obj` может измениться.                 

Чтобы имитировать неизменность объекта, можно определить свойства как доступные только для чтения (доступные для записи: false).

вар объект = {}; Объект . defineProperty ( obj , 'foo' , { значение : 'bar' , доступно для записи : false }); объект . Фу = 'бар2' ; // молча игнорируется             

Однако описанный выше подход по-прежнему позволяет добавлять новые свойства. В качестве альтернативы можно использовать Object.freeze, чтобы сделать существующие объекты неизменяемыми.

вар obj = { foo : 'bar' }; Объект . заморозить ( объект ); объект . Фу = 'бары' ; // не могу редактировать свойство, молча игнорирую obj . foo2 = 'бар2' ; // невозможно добавить свойство, молча игнорируется            

Благодаря реализации ECMA262 JavaScript получил возможность создавать неизменяемые ссылки, которые нельзя переназначить. Однако использование constобъявления не означает, что значение ссылки только для чтения является неизменным, просто имя не может быть присвоено новому значению.

const ALWAYS_IMMUTABLE = правда ;   попробуйте { ALWAYS_IMMUTABLE = ложь ; } поймать ( ошибка ) { console . log ( "Невозможно переназначить неизменяемую ссылку." ); }        const arr = [ 1 , 2 , 3 ]; обр . нажать ( 4 ); консоль . журнал ( обр ); // [1, 2, 3, 4]      

Использование неизменяемого состояния стало растущей тенденцией в JavaScript с момента появления React , который отдает предпочтение шаблонам управления состоянием, подобным Flux, таким как Redux . [12]

Перл

В Perl можно создать неизменяемый класс с помощью библиотеки Moo, просто объявив все атрибуты только для чтения:

пакет Неизменяемый ; используйте Му ;  имеет значение => ( is => 'ro' , # только для чтения по умолчанию => 'data' , # можно переопределить, предоставив конструктору # значение: Immutable->new(value => 'something else'); ) ;            1 ;

Раньше создание неизменяемого класса требовало двух шагов: во-первых, создание средств доступа (автоматически или вручную), которые предотвращают изменение атрибутов объекта, и, во-вторых, предотвращение прямого изменения данных экземпляра экземпляров этого класса (обычно это хранилось в хэше). ссылку и может быть заблокирован с помощью функции lock_hash Hash::Util):

пакет Неизменяемый ; используйте строгий ; использовать предупреждения ; используйте базу qw(Class::Accessor) ; # создаем средства доступа только для чтения __PACKAGE__ -> mk_ro_accessors ( qw(value) ); используйте Hash::Util 'lock_hash' ;       суб новый { мой $класс = сдвиг ; вернуть $class , если ref ( $class ); die "Аргументами new должны быть пары ключ => значение\n" if ( @_ % 2 == 0 ); мои %defaults = ( значение => 'данные' , ); мой $obj = { %defaults , @_ , }; благослови $obj , $class ; # предотвращаем изменение данных объекта lock_hash %$obj ; } 1 ;                                       

Или с помощью написанного вручную аксессора:

пакет Неизменяемый ; используйте строгий ; использовать предупреждения ; используйте Hash::Util 'lock_hash' ;     суб новый { мой $класс = сдвиг ; вернуть $class , если ref ( $class ); die "Аргументами new должны быть пары ключ => значение\n" if ( @_ % 2 == 0 ); мои %defaults = ( значение => 'данные' , ); мой $obj = { %defaults , @_ , }; благослови $obj , $class ; # предотвращаем изменение данных объекта lock_hash %$obj ; }                                       # подзначение средства доступа только для чтения { my $self = сдвиг ; if ( my $new_value = shift ) { # попытка установить новое значение die "Этот объект не может быть изменен\n" ; } else { return $self -> { значение } } } 1 ;                     

Питон

В Python некоторые встроенные типы (числа, логические значения, строки, кортежи, замороженные наборы) являются неизменяемыми, но пользовательские классы, как правило, изменяемы. Чтобы имитировать неизменяемость в классе, можно переопределить установку и удаление атрибута, чтобы вызвать исключения:

class  ImmutablePoint : """Неизменяемый класс с двумя атрибутами 'x' и 'y'."""  __slots__  =  [ 'x' ,  'y' ] def  __setattr__ ( self ,  * args ):  поднять  TypeError ( «Невозможно изменить неизменяемый экземпляр». ) __delattr__  =  __setattr__ def  __init__ ( self ,  x ,  y ):  # Мы больше не можем использовать self.value = value для хранения данных экземпляра  # поэтому мы должны явно вызвать суперкласс  super () . __setattr__ ( 'x' ,  x )  супер () . __setattr__ ( 'y' ,  y )

Помощники стандартной библиотеки Collections.namedtuple и typing.NamedTuple, доступные начиная с Python 3.6, создают простые неизменяемые классы. Следующий пример примерно эквивалентен приведенному выше, плюс некоторые функции, подобные кортежу:

от  ввода  импорта  коллекций импорта NamedTuple Точка  =  коллекции . именованныйтупле ( 'Точка' ,  [ 'x' ,  'y' ])# следующее создает аналогичный именованный кортеж для вышеуказанного класса  Point ( NamedTuple ):  x :  int  y :  int

Классы данных, представленные в Python 3.7, позволяют разработчикам эмулировать неизменность с помощью замороженных экземпляров. Если создан замороженный класс данных, dataclassesон будет переопределяться __setattr__()и __delattr__()подниматься FrozenInstanceErrorпри вызове.

из  классов данных  импортировать  класс данных@dataclass ( Frozen = True ) класс  Точка :  x :  int  y :  int

Ракетка

Racket существенно отличается от других реализаций Scheme , делая тип базовой пары («минус-ячейки») неизменяемым. Вместо этого он предоставляет параллельный изменяемый парный тип через mcons, mcarи set-mcar!т. д. Кроме того, поддерживаются многие неизменяемые типы, например неизменяемые строки и векторы, и они широко используются. Новые структуры по умолчанию являются неизменяемыми, если только поле специально не объявлено изменяемым или вся структура:

( структура foo1 ( x y )) ; все поля неизменяемы ( struct foo2 ( x [ y #:mutable ])) ; одно изменяемое поле ( struct foo3 ( x y ) #:mutable ) ; все поля изменяемы              

Язык также поддерживает функционально реализованные неизменяемые хэш-таблицы и неизменяемые словари.

Ржавчина

Система владения Rust позволяет разработчикам объявлять неизменяемые переменные и передавать неизменяемые ссылки. По умолчанию все переменные и ссылки являются неизменяемыми. Изменяемые переменные и ссылки создаются явно с помощью mutключевого слова.

Постоянные элементы в Rust всегда неизменяемы.

// константные элементы всегда неизменяемы const ALWAYS_IMMUTABLE : bool = true ;   struct  Object { x : использовать размер , y : использовать размер , }   fn  main () { // явно объявляем изменяемую переменную let mut mutable_obj = Object { x : 1 , y : 2 }; mutable_obj . х = 3 ; // хорошо                let mutable_ref = & mut mutable_obj ; mutable_ref . х = 1 ; // хорошо         пусть immutable_ref = & mutable_obj ; immutable_ref . х = 3 ; // ошибка E0594        // по умолчанию переменные неизменяемы let immutable_obj = Object { x : 4 , y : 5 }; immutable_obj . х = 6 ; // ошибка E0596             let mutable_ref2 = & mut immutable_obj ; // ошибка E0596       пусть immutable_ref2 = & immutable_obj ; immutable_ref2 . х = 6 ; // ошибка E0594 }        

Скала

В Scala любая сущность (в узком смысле — привязка) может быть определена как изменяемая или неизменяемая: в объявлении можно использовать val(value) для неизменяемых сущностей и var(variable) для изменяемых. Обратите внимание: даже если неизменяемую привязку нельзя переназначить, она все равно может ссылаться на изменяемый объект, и для этого объекта по-прежнему можно вызывать изменяющие методы: привязка является неизменяемой, но базовый объект может быть изменяемым.

Например, следующий фрагмент кода:

val maxValue = 100 вар currentValue = 1      

определяет неизменяемую сущность maxValue(целочисленный тип определяется во время компиляции) и изменяемую сущность с именем currentValue.

По умолчанию классы коллекций, такие как Listи Map, являются неизменяемыми, поэтому методы обновления возвращают новый экземпляр, а не изменяют существующий. Хотя это может показаться неэффективным, реализация этих классов и их гарантии неизменяемости означают, что новый экземпляр может повторно использовать существующие узлы, что, особенно в случае создания копий, очень эффективно. [13] [ нужен лучший источник ]

Смотрите также

Рекомендации

Эта статья содержит некоторые материалы из Книги шаблонов проектирования Perl.

  1. ^ «неизменяемое прилагательное - Определение, изображения, произношение и примечания по использованию - Оксфордский словарь для продвинутых учащихся на сайте OxfordLearnersDictionaries.com» . www.oxfordlearnersdictionaries.com .
  2. ^ abc Goetz et al. Параллелизм Java на практике . Addison Wesley Professional, 2006, раздел 3.4. Неизменность
  3. ^ «6.005 — Создание программного обеспечения».
  4. ^ Дэвид О'Мира (апрель 2003 г.). «Изменяемые и неизменяемые объекты: убедитесь, что методы не могут быть переопределены». Яванское ранчо . Проверено 14 мая 2012 г. Предпочтительный способ — сделать класс финальным. Иногда это называют «сильной неизменностью». Это не позволяет кому-либо расширить ваш класс и случайно или намеренно сделать его изменяемым.
  5. ^ Аб Блох, Джошуа (2018). «Эффективная Java: Руководство по языку программирования» (третье изд.). Аддисон-Уэсли. ISBN 978-0134685991.
  6. ^ «Встроенные функции — документация Python v3.0» . docs.python.org .
  7. Скит, Джон (23 марта 2019 г.). C# в глубине . Мэннинг. ISBN 978-1617294532.
  8. ^ ab D Спецификация языка § 18
  9. ^ D Спецификация языка § 12.16 (Термины массив и срез используются как взаимозаменяемые.)
  10. ^ «Как создать неизменяемый класс и объект в Java - пример учебника» . Javarevisited.blogspot.co.uk. 04.03.2013 . Проверено 14 апреля 2014 г.
  11. ^ «Неизменяемые объекты». javapractices.com . Проверено 15 ноября 2012 г.
  12. ^ «Неизменяемость в JavaScript: противоположный взгляд». Десалас работает .
  13. ^ «API коллекций Scala 2.8 — конкретные неизменяемые классы коллекций» . Scala-lang.org . Проверено 14 апреля 2014 г.

Внешние ссылки