Jump to content

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

(Перенаправлено с Mutable )

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

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

Концепции

[ редактировать ]

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

[ редактировать ]

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

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

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

[ редактировать ]

Иногда говорят о неизменяемости определенных полей объекта. Это означает, что невозможно изменить эти части состояния объекта, даже если другие части объекта могут быть изменяемыми ( слабо неизменяемыми ). Если все поля неизменяемы, то объект является неизменяемым. Если весь объект не может быть расширен другим классом, объект называется строго неизменяемым . [4] Это может, например, помочь явно обеспечить соблюдение определенных инвариантов в отношении определенных данных в объекте, которые останутся неизменными на протяжении всего времени существования объекта. В некоторых языках это делается с помощью ключевого слова (например, const в С++ , 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 есть изменяемые версии строк. На Яве [5] : 84  это StringBuffer и StringBuilder (изменяемые версии Java String), а в .NET это StringBuilder (изменяемая версия .Net String). Python 3 имеет вариант изменяемой строки (байтов) с именем bytearray. [6]

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

Похожие шаблоны — Immutable Interface и Immutable Wrapper .

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

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

  type Some_type is new Integer; -- could be anything more complicated
  x: constant Some_type:= 1; -- immutable
  y: Some_type; -- mutable

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

  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# вы можете обеспечить неизменность полей класса с помощью readonly заявление. [7] : 239  Сделав все поля неизменяемыми, вы получите неизменяемый тип.

class AnImmutableType
{
    public readonly double _value;
    public AnImmutableType(double x) 
    { 
        _value = x; 
    }
    public AnImmutableType Square() 
    { 
        return new AnImmutableType(_value * _value); 
    }
}

В C# есть неизменяемые записи. [8] [9]

record Person(string FirstName, string LastName);

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

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_;
};

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

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

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 существуют два квалификатора типа : const и immutable, для переменных, которые нельзя изменить. [10] В отличие от C++ const, Ява finalи C# readonly, они транзитивны и рекурсивно применяются ко всему, что достижимо через ссылки на такую ​​переменную. Разница между const и immutable это то, к чему они применяются: const является свойством переменной: на указанное значение могут легально существовать изменяемые ссылки, т. е. значение может фактически измениться. В отличие, immutable является свойством указанного значения: значение и все, что транзитивно достижимо из него, не может измениться (без нарушения системы типов, что приводит к неопределенному поведению ). Любая ссылка на это значение должна быть отмечена const или immutable. В основном для любого неквалифицированного типа T, const(T) представляет собой непересекающийся союз T (изменяемый) и immutable(T).

class C {
  /*mutable*/ Object mField;
    const     Object cField;
    immutable Object iField;
}

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

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

void func(C m, const C c, immutable C i)
{ /* inside the braces */ }

Внутри брекетов, 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) ценности.

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

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

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

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

String s = "ABC";
s.toLowerCase(); // This accomplishes nothing!

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

s = s.toLowerCase();

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

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

Переменные примитивного типа ( int, long, shortи т. д.) можно переназначить после определения. Этого можно избежать, используя 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

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

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

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

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

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.

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

var obj = {};
Object.defineProperty(obj, 'foo', { value: 'bar', writable: false });
obj.foo = 'bar2'; // silently ignored

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

var obj = { foo: 'bar' };
Object.freeze(obj);
obj.foo = 'bars'; // cannot edit property, silently ignored
obj.foo2 = 'bar2'; // cannot add property, silently ignored

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

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]

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

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

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;

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

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;

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

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;

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

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)

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

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

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

from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
    x: int
    y: int

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

(struct foo1 (x y))             ; all fields immutable
(struct foo2 (x [y #:mutable])) ; one mutable field
(struct foo3 (x y) #:mutable)   ; all fields mutable

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

Ржавчина

[ редактировать ]

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

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

// 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 любую сущность (в узком смысле, привязку) можно определить как изменяемую или неизменяемую: в объявлении можно использовать val (значение) для неизменяемых сущностей и var (переменная) для изменяемых. Обратите внимание: даже если неизменяемую привязку нельзя переназначить, она все равно может ссылаться на изменяемый объект, и для этого объекта по-прежнему можно вызывать изменяющие методы: привязка является неизменной, но базовый объект может быть изменяемым.

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

val maxValue = 100
var currentValue = 1

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

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

См. также

[ редактировать ]

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

  1. ^ «неизменяемое прилагательное — определение, изображения, произношение и примечания по использованию — Оксфордский словарь для продвинутых учащихся на сайте OxfordLearnersDictionaries.com» . www.oxfordlearnersdictionaries.com .
  2. ^ Jump up to: а б с Гетц и др. Параллелизм Java на практике . Addison Wesley Professional, 2006, раздел 3.4. Неизменяемость
  3. ^ «6.005 — Создание программного обеспечения» .
  4. ^ Дэвид О'Мира (апрель 2003 г.). «Изменяемые и неизменяемые объекты: убедитесь, что методы не могут быть переопределены» . Яванское ранчо . Проверено 14 мая 2012 г. Предпочтительный способ — сделать класс финальным. Иногда это называют «сильной неизменностью». Это не позволяет кому-либо расширить ваш класс и случайно или намеренно сделать его изменяемым.
  5. ^ Jump up to: а б Блох, Джошуа (2018). «Эффективная Java: Руководство по языку программирования» (третье изд.). Аддисон-Уэсли. ISBN  978-0134685991 .
  6. ^ «Встроенные функции — документация Python v3.0» . docs.python.org .
  7. ^ Скит, Джон (23 марта 2019 г.). C# в глубине . Мэннинг. ISBN  978-1617294532 .
  8. ^ «Использование типов записей — учебник C# — C#» . Learn.microsoft.com . 14 ноября 2023 г. Проверено 23 февраля 2024 г.
  9. ^ «Записи — Справочник по C# — C#» . Learn.microsoft.com . 25 мая 2023 г. Проверено 23 февраля 2024 г.
  10. ^ Jump up to: а б D Спецификация языка § 18
  11. ^ D Спецификация языка § 12.16 (Термины массив и срез используются как взаимозаменяемые.)
  12. ^ «Как создать неизменяемый класс и объект в Java – пример учебника» . Javarevisited.blogspot.co.uk. 04.03.2013 . Проверено 14 апреля 2014 г.
  13. ^ «Неизменяемые объекты» . javapractices.com . Проверено 15 ноября 2012 г.
  14. ^ «Неизменяемость в JavaScript: противоположный взгляд» . Десалас работает .
  15. ^ «API коллекций Scala 2.8 — конкретные неизменяемые классы коллекций» . Scala-lang.org . Проверено 14 апреля 2014 г.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 3444a98bde48c366255ffd805c18ca7d__1713295800
URL1:https://arc.ask3.ru/arc/aa/34/7d/3444a98bde48c366255ffd805c18ca7d.html
Заголовок, (Title) документа по адресу, URL1:
Immutable object - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)