В информатике упаковка ( также известная как обертывание) — это преобразование помещения примитивного типа в объект таким образом, чтобы значение можно было использовать в качестве ссылки . Распаковка — это обратное преобразование извлечения примитивного значения из его объекта-обертки. Автоупаковка — это термин для автоматического применения преобразований упаковки и/или распаковки по мере необходимости.
Наиболее заметное применение упаковки — в Java , где существует различие между ссылочными и значимыми типами по таким причинам, как эффективность времени выполнения, а также синтаксические и семантические проблемы. В Java a LinkedList
может хранить только значения типа Object
. Можно было бы захотеть иметь a LinkedList
из int
, но это напрямую невозможно. Вместо этого Java определяет примитивные классы-оболочки, соответствующие каждому примитивному типу : Integer
и int
, Character
и char
, Float
и float
и т. д. Затем можно определить a LinkedList
с помощью упакованного типа Integer
и вставить int
значения в список, упаковав их как Integer
объекты. (Используя универсальные параметризованные типы, представленные в J2SE 5.0, этот тип представлен как .)LinkedList<Integer>
С другой стороны, C# не имеет примитивных классов-оболочек, но допускает упаковку любого типа значения, возвращая обобщенную Object
ссылку. В Objective-C любое примитивное значение может быть дополнено префиксом a, @
чтобы сделать NSNumber
из него (например @123
, или ). Это позволяет добавлять их в любую из стандартных коллекций, например .@(123)
NSArray
В Haskell нет или почти нет понятия ссылочного типа , но по-прежнему используется термин «упакованный» для обозначения унифицированного представления указателя на помеченное объединение в системе выполнения . [1]
Упакованный объект всегда является копией объекта значения и обычно является неизменяемым . Распаковка объекта также возвращает копию сохраненного значения. Повторная упаковка и распаковка объектов может иметь серьезное влияние на производительность, поскольку упаковка динамически выделяет новые объекты, а распаковка (если упакованное значение больше не используется) затем делает их пригодными для сборки мусора . Однако современные сборщики мусора, такие как сборщик мусора Java HotSpot по умолчанию, могут более эффективно собирать недолговечные объекты, поэтому если упакованные объекты недолговечны, влияние на производительность может быть незначительным.
В некоторых языках существует прямая эквивалентность между неупакованным примитивным типом и ссылкой на неизменяемый, упакованный тип объекта. Фактически, можно заменить все примитивные типы в программе на упакованные типы объектов. В то время как присваивание одного примитива другому скопирует его значение, присваивание одной ссылки на упакованный объект другому скопирует значение ссылки для ссылки на тот же объект, что и первая ссылка. Однако это не вызовет никаких проблем, поскольку объекты неизменяемы, поэтому семантически нет никакой реальной разницы между двумя ссылками на один и тот же объект или на разные объекты (если только вы не смотрите на физическое равенство). Для всех операций, отличных от присваивания, таких как арифметические, сравнительные и логические операторы, можно распаковать упакованный тип, выполнить операцию и повторно упаковать результат по мере необходимости. Таким образом, можно вообще не хранить примитивные типы.
Автобоксинг — это термин для получения ссылочного типа из типа значения просто через преобразование типа (явное или неявное). Компилятор автоматически предоставляет дополнительный исходный код, который создает объект.
Например, в версиях Java до J2SE 5.0 следующий код не компилировался:
Integer i = new Integer ( 9 ); Integer i = 9 ; // ошибка в версиях до 5.0!
Компиляторы до версии 5.0 не принимали последнюю строку. Integer
являются ссылочными объектами, на первый взгляд ничем не отличающимися от List
, Object
и так далее. Чтобы преобразовать из в int
, Integer
нужно было «вручную» создать экземпляр объекта Integer. Начиная с J2SE 5.0, компилятор примет последнюю строку и автоматически преобразует ее так, чтобы был создан объект Integer для хранения значения 9
. [2] Это означает, что начиная с J2SE 5.0, что-то вроде , где и сами являются , теперь будет компилироваться - a и b распаковываются, целочисленные значения суммируются, а результат автоматически упаковывается в новый , который в конечном итоге сохраняется внутри переменной . Операторы равенства нельзя использовать таким образом, потому что операторы равенства уже определены для ссылочных типов, для равенства ссылок; чтобы проверить равенство значения в упакованном типе, нужно все равно вручную распаковать их и сравнить примитивы или использовать метод .Integer c = a + b
a
b
Integer
Integer
c
Objects.equals
Другой пример: J2SE 5.0 позволяет программисту обрабатывать коллекцию (такую как LinkedList
) так, как если бы она содержала int
значения вместо Integer
объектов. Это не противоречит сказанному выше: коллекция по-прежнему содержит только ссылки на динамические объекты и не может перечислять примитивные типы. Это не может быть , но вместо этого должно быть . Однако компилятор автоматически преобразует код так, что список будет «молча» получать объекты, в то время как исходный код упоминает только примитивные значения. Например, теперь программист может писать и думать так, как если бы были добавлены в список; но компилятор фактически преобразует строку в .LinkedList<int>
LinkedList<Integer>
list.add(3)
int
3
list.add(new Integer(3))
При автоматической распаковке компилятор автоматически предоставляет дополнительный исходный код, который извлекает значение из этого объекта, либо вызывая какой-либо метод для этого объекта, либо другими способами.
Например, в версиях Java до J2SE 5.0 следующий код не компилировался:
Integer k = new Integer ( 4 ); int l = k . intValue (); // всегда подходит int m = k ; // было бы ошибкой, но сейчас все в порядке
C# не поддерживает автоматическую распаковку в том же смысле, что и Java, поскольку в нем нет отдельного набора примитивных типов и типов объектов. Все типы, имеющие как примитивную, так и объектную версию в Java, автоматически реализуются компилятором C# как примитивные типы (значения) или как объектные типы (ссылки).
В обоих языках автоматическая упаковка не приводит к автоматическому приведению типов, т. е. следующий код не скомпилируется:
С#:
int i = 42 ; object o = i ; // box int j = o ; // unbox (ошибка) Console . WriteLine ( j ); // недостижимая строка, автор мог ожидать вывода "42"
Ява:
int i = 42 ; Object o = i ; // box int j = o ; // unbox (ошибка) System.out.println ( j ) ; // недостижимая строка , автор мог ожидать вывода "42 "
Современный Object Pascal имеет еще один способ выполнения операций над простыми типами, близкий к упаковке, называемый помощниками типов в FreePascal или помощниками записей в Delphi и FreePascal в режиме Delphi. Упомянутые диалекты являются языками компиляции Object Pascal в собственные языки, и поэтому не имеют некоторых функций, которые могут реализовать C# и Java. В частности, вывод типов
во время выполнения для строго типизированных переменных.
Но эта функция связана с упаковкой.
Она позволяет программисту использовать такие конструкции, как
{ $ifdef fpc}{$mode delphi}{$endif} использует sysutils ; // этот модуль содержит оболочки для простых типов var x : integer = 100 ; s : string ; begin s : = x.ToString ; writeln ( s ) ; end .