Сегодня речь пойдет об автоупаковке (autoboxing) и распаковке (unboxing). Это одно из существенных изменений, внесенных в JDK 5. Теперь разработчики могут писать более чистый код, но непонимание работы этого механизма может привести к плохой производительности.
Автоупаковка
Это автоматическая инкапсуляция примитивного типа в эквивалентную ему класс-обёртку всякий раз, когда требуется объект данного типа. Про инкапсуляцию и другие принципы ООП существует замечательная статья от ffriend.
Autoboxing происходит:
- При присвоении значения примитивного типа переменной соответствующего класса-обёртки.
- При передаче примитивного типа в параметр метода, ожидающего соответствующий ему класс-обёртку.
Примеры
До JDK 5
public class Main { public static void main(String[] args) { Integer iOb = new Integer(7); Double dOb = new Double(7.0); Character cOb = new Character('a'); Boolean bOb = new Boolean(true); method(new Integer(7)); } public static void method(Integer iOb) { System.out.println("Integer"); } }
Начиная с JDK 5
public class Main { public static void main(String[] args) { Integer iOb = 7; Double dOb = 7.0; Character cOb = 'a'; Boolean bOb = true; method(7); } public static void method(Integer iOb) { System.out.println("Integer"); } }
Автораспаковка
Это преобразование класса-обёртки в соответствующий ему примитивный тип. Если при распаковке класс-обёртка был равен null, произойдет исключение java.lang.NullPointerException.
Unboxing происходит:
- При присвоении экземпляра класса-обёртки переменной соответствующего примитивного типа.
- В выражениях, в которых один или оба аргумента являются экземплярами классов-обёрток (кроме операции == и !=).
- При передаче объекта класса-обёртки в метод, ожидающий соответствующий примитивный тип.
Давайте рассмотрит более детально.
1. При присвоении
До JDK 5
int i = iOb.intValue(); double d = dOb.doubleValue(); char c = cOb.charValue(); boolean b = bOb.booleanValue();
Начиная с JDK 5
int i = iOb; double d = dOb; char c = cOb; boolean b = bOb;
2. В выражениях
Так как арифметические операторы и операторы сравнения (исключение == и !=) применяются только к примитивным типам, приходилось делать распаковку вручную, что заметно снижало читабельность выражений, делая их громоздкими, и кода в целом.
Integer iOb1 = new Integer(5); Integer iOb2 = new Integer(7); System.out.println(iOb1.intValue() > iOb2.intValue());
Благодаря автораспаковке, мы смело можем писать выражения, не используя методы конвертации. Теперь за этим следит компилятор Java.
System.out.println(iOb1 > iOb2); System.out.println(iOb1 + iOb2);
При сравнении классов-обёрток оператором == или !=, происходит сравнение по ссылкам, а не по значениям и может возникнуть путаница. Например, как вы думаете, что выведется на экран при исполнении следующего кода?
Integer iOb1 = 100; Integer iOb2 = 100; System.out.println(iOb1 == iOb2); Integer iOb3 = new Integer(120); Integer iOb4 = new Integer(120); System.out.println(iOb3 == iOb4); Integer iOb5 = 200; Integer iOb6 = 200; System.out.println(iOb5 == iOb6);
Ответ: в первом случае — true, во втором и третьем — false.
В первом случае фактически вызывается статичный метод java.lang.Integer.valueOf(int), который кэширует значения от -128 до 127 (верхнюю границу можно изменять) и при повторном использовании достает их из так называемого pool (набор инициализированных и готовых к использованию объектов). Во втором происходит явное создание объектов, следовательно они имеют разные ссылки.
3. При передачи в метод
public class Main { public static void main(String[] args) { Integer iOb = 10; method(iOb); } public static void method(int i) { System.out.println("int"); } }
На экран будет выведено int, но стоит отметить, что если для метода реализована перегрузка с соответствующим классом-обёрткой, вызовется именно он.
public class Main { public static void main(String[] args) { Integer iOb = 10; method(iOb); } public static void method(int i) { System.out.println("int"); } public static void method(Integer iOb) { //Будет вызван данный метод System.out.println("Integer"); } }
Так же следует помнить, что автоупаковка и автораспаковка не работает для массивов.
public class Main { public static void main(String[] args) { Integer[] iObs = new Integer[] {5, 10, 50, 2, 7}; method(iObs); //Ошибка компиляции } public static void method(int ... i) { System.out.println("int[]"); } }
Плохая производительность
Классы-обёртки неизменяемые, поэтому при каждой автоупаковке (за исключением значений из pool) создается новый объект, что может привести к неразумному расходу памяти.
public static Integer sumBeforeInclusive(Integer number) { Integer iOb = number; if (number > 1) iOb += sumBeforeInclusive(number - 1); return iOb; }
Примитивные типы и их классы-обертки
- byte — Byte
- short — Short
- int — Integer
- long — Long
- float — Float
- double — Double
- char — Character
- boolean — Boolean
ссылка на оригинал статьи https://habrahabr.ru/post/329498/
Добавить комментарий