Почему мы используем автобоксирование и распаковку в Java?

81

Автобоксирование - это автоматическое преобразование, которое компилятор Java выполняет между примитивными типами и соответствующими им классами-оболочками объектов. Например, преобразование int в Integer, double в Double и так далее. Если преобразование идет другим путем, это называется распаковкой.

Так зачем нам это нужно и почему мы используем автобоксирование и распаковку в Java?

Теодорос Хатцигианнакис
источник
1
В основном для Generics ..
началок
3
Integerесть parseIntметод. intне имеет. :)
Vishal Zanzrukia
@VishalZanzrukia Так просто для большей функциональности?
12
Вы можете иметь List<Integer>, но не можете List<int>.
Vishal Zanzrukia
Да .. точно .. для большей функциональности
Vishal Zanzrukia

Ответы:

173

Необходим некоторый контекст, чтобы полностью понять основную причину этого.

Примитивы против классов

Примитивные переменные в Java содержат значения (целое число, двоичное число с плавающей запятой двойной точности и т. Д.). Поскольку эти значения могут иметь разную длину , переменные, содержащие их, также могут иметь разную длину (рассмотрим по floatсравнению с double).

С другой стороны, переменные класса содержат ссылки на экземпляры. Ссылки обычно реализуются как указатели (или что-то очень похожее на указатели) на многих языках. Эти вещи , как правило , имеют одинаковый размер, независимо от размеров случаев они относятся ( Object, String, Integerи т.д.).

Это свойство переменных класса делает ссылки, которые они содержат, взаимозаменяемыми (в определенной степени). Это позволяет нам делать то, что мы называем подстановкой : вообще говоря, использовать экземпляр определенного типа как экземпляр другого, связанного типа (например, использовать a Stringкак an Object).

Примитивные переменные не взаимозаменяемы одинаково ни друг с другом, ни сObject . Наиболее очевидная причина (но не единственная) - разница в размерах. Это делает примитивные типы неудобными в этом отношении, но они все еще нужны нам в языке (по причинам, которые в основном сводятся к производительности).

Дженерики и стирание типов

Универсальные типы - это типы с одним или несколькими параметрами типа (точное число называется универсальной арностью ). Например, определение универсального типа List<T> имеет параметр типа T, который может быть Object(создание конкретного типа List<Object> ), String( List<String>), Integer( List<Integer>) и так далее.

Универсальные типы намного сложнее неуниверсальных. Когда они были представлены Java (после ее первоначального выпуска), чтобы избежать радикальных изменений JVM и возможного нарушения совместимости со старыми двоичными файлами, создатели Java решили реализовать универсальные типы наименее инвазивным способом: все конкретные типы List<T>на самом деле скомпилированы в (двоичный эквивалент) List<Object>(для других типов граница может быть чем-то другим Object, но вы понимаете). В этом процессе теряется общая информация о параметрах арности и типа , поэтому мы называем это стиранием типа .

Соединение двух вместе

Теперь проблема заключается в сочетании перечисленных выше реалий: если List<T>становится List<Object>во всех случаях, то Tвсегда должен быть тип, которому можно напрямую присвоитьObject . Ничего другого нельзя допускать. Поскольку, как мы уже говорили ранее, int, floatи doubleне являются взаимозаменяемыми Object, может не быть List<int>, List<float>илиList<double> (если значительно более сложная реализация дженериков не существовало в JVM).

Но Java типы таких предложений Integer, Floatи Doubleкоторые обернуть эти примитивы в экземпляры класса, что делает их эффективно взаимозаменяемы , как Object, таким образом , позволяя общие типы , косвенно работы с примитивами , как хорошо (потому что вы можете иметь List<Integer>, List<Float>,List<Double> и так далее).

Процесс создания Integer из inta, Floata floatи т. Д. Называется боксом . Обратное называется распаковкой . Поскольку необходимость упаковывать примитивы каждый раз, когда вы хотите их использовать Object, неудобно, бывают случаи, когда язык делает это автоматически - это называется автобоксингом .

Теодорос Хатцигианнакис
источник
Согласно вашему объяснению, нам нужны эти классы Integer, String, ... для реализации дженериков. Есть ли другая причина?
Бишвас Мишра
1
@BishwasMishra У нас есть они, чтобы мы могли использовать эти значения в качестве Objectэкземпляров. Обобщение через стирание типа - одно из применений этого.
Теодорос Хатцигианнакис
16

Авто бокс будет использоваться для преобразования примитивных типов данных к их объектам класса обертки. Класс Wrapper предоставляет широкий спектр функций, выполняемых над примитивными типами. Самый распространенный пример:

int a = 56;
Integer i = a; // Auto Boxing

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

Auto Boxing также пригодится, когда мы работаем с типами java.util.Collection. Когда мы хотим создать коллекцию примитивных типов, мы не можем напрямую создать коллекцию примитивного типа, мы можем создать коллекцию только объектов. Например :

ArrayList<int> al = new ArrayList<int>(); // not supported 

ArrayList<Integer> al = new ArrayList<Integer>(); // supported 
al.add(45); //auto Boxing 

Классы обертки

Каждый из 8 примитивных типов Java (byte, short, int, float, char, double, boolean, long) имеет отдельный класс Wrapper, связанный с ними. Этот класс Wrapper имеет предопределенные методы для выполнения полезных операций с примитивными типами данных.

Использование классов оболочки

String s = "45";
int a = Integer.parseInt(s); // sets the value of a to 45.

Классы Wrapper предоставляют множество полезных функций. Ознакомьтесь с документацией по Java здесь

Распаковка противоположна Auto Boxing, где мы преобразуем объект класса оболочки обратно в его примитивный тип. JVM делает это автоматически, поэтому мы можем использовать классы-оболочки для определенных операций, а затем преобразовывать их обратно в примитивные типы, поскольку примитивы приводят к более быстрой обработке. Например :

Integer s = 45;
int a = s; auto UnBoxing;

В случае Коллекций, которые работают с объектами, используется только автоматическая распаковка. Вот как :

ArrayList<Integer> al = new ArrayList<Integer>();
al.add(45);

int a = al.get(0); // returns the object of Integer . Automatically Unboxed . 
Варун
источник
4

Примитивные (не объектные) типы имеют здесь свою эффективность.

Примитивные типы int, boolean, double- это непосредственные данные, а Objects - ссылки. Следовательно, поля (или переменные)

int i;
double x;
Object s;

потребуется локальная память 4 + 8 + 8? где для объекта хранится только ссылка (адрес) на память.

Используя обертки Object Integer, Doubleи другие, можно было бы ввести косвенную ссылку на некоторый экземпляр Integer / Double в памяти кучи.

Зачем нужен бокс?

Это вопрос относительного масштаба. Планируется, что в будущем Java будет иметь возможность ArrayList<int>поднимать примитивные типы.

Ответ: на данный момент ArrayList работает только для объекта, резервируя место для ссылки на объект и аналогичным образом управляя сборкой мусора. Следовательно, универсальные типы являются дочерними объектами. Итак, если кому-то нужен ArrayList значений с плавающей запятой, нужно было обернуть двойное значение в объект Double.

Здесь Java отличается от традиционного C ++ своими шаблонами: классы C ++ vector<string>, vector<int> создают два продукта компиляции. В Java-дизайне использовался один ArrayList.class, и для каждого типа параметра не требовался новый скомпилированный продукт.

Таким образом, без упаковки в объект нужно было бы компилировать классы для каждого вхождения типа параметра. Конкретно: каждой коллекции или классу контейнера потребуется версия для Object, int, double, boolean. Версия для Object будет обрабатывать все дочерние классы.

Фактически, потребность в такой диверсификации уже существовала в Java SE для IntBuffer, CharBuffer, DoubleBuffer, ... которые работают с int, char, double. Это было решено хакерским способом путем генерации этих исходников из общего.

Юп Эгген
источник
4

Начиная с JDK 5, в java добавлены две важные функции: автобоксинг и автоотборка. AutoBoxing - это процесс, для которого примитивный тип автоматически инкапсулируется в эквивалентную оболочку всякий раз, когда требуется такой объект. Вам не нужно явно создавать объект. Автоматическая распаковка - это процесс, при котором значение инкапсулированного объекта автоматически извлекается из оболочки типа, когда требуется его значение. Вам не нужно вызывать такие методы, как intValue () или doubleValue () .

Добавление автобокса и автоматической распаковки значительно упрощает алгоритмы записи , устраняя приманку ручную упаковку и распаковку значений. Также полезно избегать ошибок . Это также очень важно для дженериков , которые работают только с объектами. Наконец, автобокс облегчает работу с Collections Framework .

Амарилдо
источник
2

почему у нас (не) бокс?

сделать написание кода, в котором мы смешиваем примитивы и их объектно-ориентированные (OO) альтернативы, более удобным / менее подробным.

почему у нас есть примитивы и их объектно-ориентированные альтернативы?

примитивные типы не являются классами (в отличие от C #), поэтому они не являются подклассами Objectи не могут быть переопределены.

у нас есть примитивы, например, intпо соображениям производительности, и Objectальтернативы, например, Integerдля преимуществ объектно-ориентированного программирования, и, как второстепенный момент, чтобы иметь хорошее место для служебных констант и методов (Integer.MAX_VALUE иInteger.toString(int) ).

Преимущества объектно-ориентированного программирования легче всего увидеть с помощью Generics ( List<Integer>), но не ограничиваются этим, например:

Number getMeSome(boolean wantInt) {

    if (wantInt) {
        return Integer.MAX_VALUE;
    } else {
        return Long.MAX_VALUE;
    }
}
Hoijui
источник
1

Некоторые структуры данных могут принимать только объекты, но не примитивные типы.

Пример: ключ в HashMap.

См. Этот вопрос для получения дополнительной информации: HashMap и int как ключ

Есть и другие веские причины, например, поле "int" в базе данных, которое также может иметь значение NULL. Тип int в Java не может быть нулевым; целочисленная ссылка может. Автобоксирование и распаковка позволяют избежать написания постороннего кода при преобразованиях туда и обратно.

Габриэль
источник
0

Потому что они разных типов и для удобства. Вероятно, производительность является причиной наличия примитивных типов.

Скотт Хантер
источник
0

ArrayList не поддерживает примитивные типы, только класс поддержки. но нам нужно использовать примитивные типы, например int, double и т. д.

ArrayList<String> strArrayList = new ArrayList<String>(); // is accepted.

ArrayList<int> intArrayList = new ArrayList<int>(); // not accepted.

Класс Integer оборачивает значение примитивного типа int в объект, поэтому следующий код принимается.

ArrayList<Integer> intArrayList = new ArrayList<Integer>(); // is accepted.

мы можем добавить значение с помощью метода add (value). Чтобы добавить строковое значение, скажите «Привет» в коде strArrayList, это просто

strArrayList.add("Hello");  

и добавьте значение int, скажем 54, мы можем написать

intArrayList.add(54);

но когда мы пишем intArrayList.add (54); компилятор преобразовать в следующую строку

intArrayList.add(Integer.valueOf(54)); 

Поскольку intArrayList.add (54) проще и более приемлемо со стороны пользователя, компилятор выполняет тяжелую работу, а intArrayList.add(Integer.valueOf(54));именно - autoBoxing.

Аналогично, чтобы получить значение, мы просто набираем intArrayList.get (0), и компилятор преобразует его в <code>intArrayList.get(0).intValue();autoUnboxing.

Чхалма Султана Чхая
источник
0

Автобоксинг: преобразование примитивного значения в объект соответствующего класса-оболочки.

Распаковка: преобразование объекта типа оболочки в соответствующее примитивное значение

// Java program to illustrate the concept 
// of Autoboxing and Unboxing 
import java.io.*; 

class GFG 
{ 
    public static void main (String[] args) 
    { 
        // creating an Integer Object 
        // with value 10. 
        Integer i = new Integer(10); 

        // unboxing the Object 
        int i1 = i; 

        System.out.println("Value of i: " + i); 
        System.out.println("Value of i1: " + i1); 

        //Autoboxing of char 
        Character gfg = 'a'; 

        // Auto-unboxing of Character 
        char ch = gfg; 
        System.out.println("Value of ch: " + ch); 
        System.out.println("Value of gfg: " + gfg); 

    } 
} 
Яш Патель
источник