DSergey_Kh
12 уровень

Автоупаковка и распаковка в Java.

Пост из группы Архив info.javarush.ru
3272 участников
В этой статье мы рассмотрим такую функцию в Java как автоупаковка/распаковка. Автоупаковка и распаковка это функция преобразования примитивных типов в объектные и наоборот. Весь процесс выполняется автоматически средой выполнения Java (JRE). Но следует быть осторожным при реализации этой функции, т.к. Она может влиять на производительность вашей программы. Примитивные типы Классы оболочки boolean Boolean byte Byte char Character float Float int Integer long Long short Short Таблица 1: Примитивные типы и эквивалентные им классы оболочки С операторами сравнения Автоупаковка и распаковка могут использоваться с операторами сравнения. Следующий фрагмент кода иллюстрирует, как это происходит: Листинг 3: Пример кода, показывающий автоупаковку и распаковку с оператором сравнения 1 public class BoxedComparator { 2 public static void main(String[] args) { 3 Integer in = new Integer(25); 4 if (in < 35) 5 System.out.println("Value of int = " + in); 6 } 7 } Автоупаковка и распаковка при перегрузке метода Автоупаковка и распаковка выполняется при перегрузке метода на основании следующих правил: - Расширение "побеждает" упаковку - В ситуации, когда становится выбор между расширением и упаковкой, расширение предпочтительней. Листинг 4: Пример кода, показывающий преимущество перегрузки 1 public class WideBoxed { 2 public class WideBoxed { 3 static void methodWide(int i) { 4 System.out.println("int"); 5 } 6 7 static void methodWide( Integer i ) { 8 System.out.println("Integer"); 9 } 10 11 public static void main(String[] args) { 13 short shVal = 25; 14 methodWide(shVal); 15 } 16 } 17 } Вывод программы - тип int - Расширение побеждает переменное количество аргументов – В ситуации, когда становится выбор между расширением и переменным количеством аргументов, расширение предпочтительней. Листинг 5: Пример кода, показывающий преимущество перегрузки 1 public class WideVarArgs { 2 3 static void methodWideVar(int i1, int i2) { 4 System.out.println("int int"); 5 } 6 7 static void methodWideVar(Integer... i) { 8 System.out.println("Integers"); 9 } 10 11 public static void main( String[] args) { 12 short shVal1 = 25; 13 short shVal2 = 35; 14 methodWideVar( shVal1, shVal2); 15 } 16 } - Упаковка побеждает переменное количество аргументов – В ситуации, когда становится выбор между упаковкой и переменным количеством аргументов, упаковка предпочтительней. Листинг 6: Пример кода, показывающий преимущество перегрузки 1 public class BoxVarargs { 2 static void methodBoxVar(Integer in) { 3 System.out.println("Integer"); 4 } 5 6 static void methodBoxVar(Integer... i) { 7 System.out.println("Integers"); 8 } 9 public static void main(String[] args) { 10 int intVal1 = 25; 11 methodBoxVar(intVal1); 12 } 13 } Вы должны помнить о следующих вещах, используя Автоупаковку: Как мы знаем, любая хорошая функция имеет недостаток. Автоупаковка не является исключением в этом отношении. Некоторый важные замечания, которые должен учитывать разработчик при использовании этой функции: - Сравнивая объекты оператором ‘==’ может возникнуть путаница, так как он может применяться к примитивным типам и объектам. Когда этот оператор применяется к объектам,он фактически сравнивает ссылки на объекты а не сами объекты. Листинг 7: Пример кода, показывающий сравнение. 1 public class Comparator { 2 public static void main(String[] args) { 3 Integer istInt = new Integer(1); 4 Integer secondInt = new Integer(1); 5 6 if (istInt == secondInt) { 7 System.out.println("both one are equal"); 8 9 } else { 10 System.out.println("Both one are not equal"); 11 } 12 } 13 } - Смешивание объектов и примитивных типов с оператором равенства и отношения. Если мы сравниваем примитивный тип с объектом, то происходит распаковывание объекта, который может бросить NullPointerException если объект null. - Кэширование объектов . Метод valueOf() создает контейнер примитивных объектов, которые он кэширует. Поскольку значения кэшируются в диапазоне от -128 до 127 включительно , эти кэшируемые объекты могут себя вести по-разному. - Ухудшение производительности. Автоупаковка или распаковка ухудшают производительность приложения, поскольку это создает нежелательный объект, из-за которого сборщику мусора приходится работать чаще. Недостатки Автоупаковки. Хотя Автоупаковка имеет ряд преимуществ, она имеет следующие недостатки: Листинг 8: Пример кода, показывающий проблему производительности. 1 public int sumEvenNumbers(List intList) { 2 int sum = 0; 3 for (Integer i : intList) { 4 if (i % 2 == 0) { 5 sum += i; 6 } 7 } 8 return sum; 9 } В этом участке кода, sum +=i будет расширен на sum = sum + i . Начиная с оператора ‘+’ JVM запускает распаковку, так как оператор ‘+’ не может применяться к объекту Integer. А затем результат автоупаковывается обратно. До версии JDK 1.5 типы данных int и Integer различались. В случае перегрузки метода эти два типа использовались без проблем. С появление автоматической упаковки/распаковки это стало сложней. Примером этого является перегруженный метод remove() в ArrayList. Класс ArrayList имеет два метода удаления - remove (index) и remove (object). В этом случае перегрузка методов не произойдет и соответствующий метод будет вызываться с соответствующими параметрами.
Заключение.
Автоупаковка является механизмом для скрытого преобразования примитивных типов данных в соответствующие классы-оболочки(объекты). Компилятор использует метод valueOf() чтобы преобразовать примитивные типы в объекты, а методы IntValue (), doubleValue () и т.д., чтобы получить примитивные типы объекта. Автоупаковка преобразует логический тип boolean в Boolean, byte в Byte, char в Character, float в Float, int в Integer, long в Long, short в Short. Распаковка происходит в обратном направлении. Оригинал статьи
Комментарии (4)
  • популярные
  • новые
  • старые
Для того, что бы оставить комментарий вы должны авторизироваться
artie 19 уровень
25 марта 2015, 18:04
Касательно пятого листинга, там не совсем правда. Если сделать так:
public class WideVarArgs {

 //     static void methodWideVar(int i1, int i2) {
 //     System.out.println("int int");
 //   }

    static void methodWideVar(Integer... i) {
       System.out.println("Integers");
    }

   public static void main( String[] args) {
       short shVal1 = 25;
      short shVal2 = 35;
     methodWideVar( shVal1, shVal2);
   }
  }

выдаст ошибку (java.lang.Integer...) cannot be applied to (short, short).
Litle 9 уровень, Москва
25 марта 2015, 21:08
Смею предположить (если есть что поправить, то будет гуд)
Когда компилятор встречает вызов метода со списком параметров переменной длины, то он упаковывает эти элементы в массив. Т.е. на входе должен быть массив Integer[]. Мы подаем short[]. При этом для массивов
autoboxing не работает: stackoverflow.com/questions/1770894/java-why-isnt-autoboxing-happening-here.

В примере кода изменяем сигнатуру вызываемого метода на int... и всё скомпилируется.
4joke 18 уровень
11 ноября 2014, 11:05
Кажется тут фигурные скобки у цикла пропущены.
Листинг 1: Простой код, показывающий автоупаковку

1 public int sumEvenNumbers(List<Integer> intList ) {
2    int sum = 0;
3    for (Integer i: intList )
4      if ( i % 2 == 0 )
5        sum += i;
6      return sum;
7 }
CynepHy6 34 уровень, Великий Новгород
11 ноября 2014, 13:34
а если так?)
public int sumEvenNumbers(List<Integer> intList ) {
    int sum = 0;
    for (Integer i: intList )
        if ( i % 2 == 0 )
            sum += i;
    return sum;
}