JavaRush /Java блог /Архив info.javarush /Автоупаковка, автораспаковка
tennisru
32 уровень

Автоупаковка, автораспаковка

Статья из группы Архив info.javarush
В J2SE 5 были введены многочисленные возможности. Одна из этих функциональностей - автоупаковка(Autoboxing) и автораспаковка(Unboxing), возможность, которую я использую почти ежедневно, даже не зная об этом. Это часто удобный(особенно когда используются коллекции), но однажды происходят неприятные сюрпризы. В этом блоге я покажу старый(но интересный для меня) случай с ошибкой NoSuchMethodError, получающий из-за смешивании классов, скомпилированных до автоупаковки/автораспаковки, с классами скомпилированных после. Следующий код демонстрирует класс(Sum), мог бы быть написан до J2SE 5. Он перегружает метод add, так чтобы он смог добавлять в итоговую сумму различные типы данных, переданные в него. import java.util.ArrayList; public class Sum { private double sum = 0; public void add(short newShort) { sum += newShort; } public void add(int newInteger) { sum += newInteger; } public void add(long newLong) { sum += newLong; } public void add(float newFloat) { sum += newFloat; } public void add(double newDouble) { sum += newDouble; } public String toString() { return String.valueOf(sum); } } До unboxing и autoboxing, любой клиент использующий Sum должен будет обеспечить примитивы для методов add или если они имеют возможность перевести объекты в примитивные типы, они должны будут сделать это, до вызовов методов add. Пример как это можно реализовать: private static String sumReferences( final Long longValue, final Integer intValue, final Short shortValue) { final Sum sum = new Sum(); if (longValue != null) { sum.add(longValue.longValue()); } if (intValue != null) { sum.add(intValue.intValue()); } if (shortValue != null) { sum.add(shortValue.shortValue()); } return sum.toString(); } J2SE 5 boxing решило эту проблему с большим количеством кода. Unboxing позволяет автоматически переводит ссылочный тип в соответствующий ему тип данных, autoboxing наоборот тип данных передает в соответствующий ему тип данных. На section можно посмотреть какие примитивные типы можно использовать и какие ссылочные типы им соответствуют. Автоупаковка, автораспаковка - 1 Изменена предыдущая программа с функцией "автоматическая распаковка", но по-прежнему необходимо избегать появления NullPointerException. private static String sumReferences( final Long longValue, final Integer intValue, final Short shortValue) { final Sum sum = new Sum(); if (longValue != null) { sum.add(longValue); } if (intValue != null) { sum.add(intValue); } if (shortValue != null) { sum.add(shortValue); } return sum.toString(); } Проверка на null не делает наш код более читаемым, поэтому попробуем изменить исходный класс Sum, то есть, если мы заменим примитивные типы на ссылочные, то проверку будет делать только класс Sum. Тогда им можно легче пользоваться. import java.util.ArrayList; public class Sum { private double sum = 0; public void add(Short newShort) { if (newShort != null) { sum += newShort; } } public void add(Integer newInteger) { if (newInteger != null) { sum += newInteger; } } public void add(Long newLong) { if (newLong != null) { sum += newLong; } } public void add(Float newFloat) { if (newFloat != null) { sum += newFloat; } } public void add(Double newDouble) { if (newDouble != null) { sum += newDouble; } } public String toString() { return String.valueOf(sum); } } Изменение API класса Sum(изменение параметров вызова функции) может привести к ошибке NoSuchMethodErrors, если какой-то вовлеченный класс(класс-клиент или одна из версий Sum) скомпилирована на другой версии Java. В частности, если класс-клиент использует примитивы и скомпилирован на Java 4 или предыдущих версиях, а Sum на более поздних версиях(ожидая ссылки, а не примитивного класса), то NoSuchMethodErrors будет возникать(где "S" обозначает, что метод Add ожидает примитивного типа short,а "V" что метод возвращает void) Exception in thread "main" java.lang.NoSuchMethodError: Sum.add(S)V at Main.main(Main.java:9) С другой стороны, если клиент скомпилирован на java 5 или позже и имеет обращается к Sum ссылочными переменными, а Sum обрабатывает только примитивные типы данных, то возникнет NoSuchMethodErrors. Exception in thread "main" java.lang.NoSuchMethodError: Sum.add(Ljava/lang/Short;)V at Main.main(Main.java:9) Несколько замечаний и напоминаний для java разработчиков, которые возникают из статьи.
    Classpaths необходим .class классы скомпилированные на одинаковых версиях java избежали бы проблемы, возникшей в этом посте Classpaths должен быть максимально скудным, чтобы уменьшить/избежать возможность получения случайных “старых” определений класса. Операции сборки должны очистить устаревшие классы полностью, и она должна перестраивать нужные классы. Автоупаковка и автораспаковка часто очень удобны, но может привести к некоторым проблемам, если не помнить о некоторых правилах. В этом посте, по-прежнему необходимо проверять сравнение объекта с null при распаковывании, так как переводим ссылочный тип в примитивный. Один из способов для определения на какой версии java был скомпилирован класс: использовать javap -verbose и взглянуть на соответствующую строку с major version и узнать версию java.
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ