Dlaczego używamy autoboxingu i unboxingu w Javie?

81

Autoboxing to automatyczna konwersja dokonywana przez kompilator języka Java między typami pierwotnymi i odpowiadającymi im klasami opakowania obiektów. Na przykład konwersja int na Integer, double na Double i tak dalej. Jeśli konwersja przebiega w drugą stronę, nazywa się to rozpakowywaniem.

Dlaczego więc tego potrzebujemy i dlaczego używamy autoboxingu i unboxingu w Javie?

Theodoros Chatzigiannakis
źródło
1
Zasadniczo dla
leków
3
Integermieć parseIntmetodę. intnie ma. :)
Vishal Zanzrukia,
@VishalZanzrukia Więc tylko po to, aby uzyskać większą funkcjonalność?
12
Możesz mieć List<Integer>, ale nie możesz List<int>.
Vishal Zanzrukia,
Tak… właśnie… aby uzyskać większą funkcjonalność
Vishal Zanzrukia,

Odpowiedzi:

173

Aby w pełni zrozumieć główny powód takiego stanu rzeczy, potrzebny jest kontekst.

Prymitywy a klasy

Zmienne pierwotne w Javie zawierają wartości (liczbę całkowitą, liczbę binarną zmiennoprzecinkową podwójnej precyzji itp.). Ponieważ te wartości mogą mieć różne długości , zawierające je zmienne mogą również mieć różne długości (rozważ w floatporównaniu double).

Z drugiej strony zmienne klasowe zawierają odniesienia do instancji. Odnośniki są zwykle implementowane jako wskaźniki (lub coś bardzo podobnego do wskaźników) w wielu językach. Te rzeczy mają zwykle ten sam rozmiar, niezależnie od wielkości tych przypadkach, których dotyczą ( Object, String, Integer, etc).

Ta właściwość zmiennych klasowych sprawia, że ​​zawarte w nich odwołania są wymienne (do pewnego stopnia). To pozwala nam robić to, co nazywamy substytucją : mówiąc ogólnie, używać instancji określonego typu jako instancji innego, pokrewnego typu ( na przykład użyj a Stringjako Object).

Zmienne pierwotne nie są zamienne w ten sam sposób, ani między sobą, ani z Object. Najbardziej oczywistym powodem tego (ale nie jedynym) jest różnica w wielkości. To sprawia, że ​​typy prymitywne są pod tym względem niewygodne, ale nadal potrzebujemy ich w języku (z powodów, które sprowadzają się głównie do wydajności).

Typy ogólne i usuwanie typów

Typy ogólne to typy z co najmniej jednym parametrem typu (dokładna liczba nazywana jest liczbą ogólną ). Na przykład definicja typu ogólnego List<T> ma parametr typu T, którym może być Object(tworzenie konkretnego typu List<Object> ), String( List<String>), Integer(List<Integer> ) i tak dalej.

Typy ogólne są o wiele bardziej skomplikowane niż nieogólne. Kiedy zostały wprowadzone do Javy (po jej pierwszym wydaniu), aby uniknąć radykalnych zmian w JVM i być może zerwania kompatybilności ze starszymi plikami binarnymi, twórcy Javy zdecydowali się zaimplementować typy generyczne w jak najmniej inwazyjny sposób: wszystkie konkretne typy List<T>są w rzeczywistości skompilowane do (binarnego odpowiednika) List<Object>(dla innych typów powiązanie może być czymś innym niż Object, ale o co chodzi). W procesie tym następuje utrata ogólnych informacji o wartościach i parametrach typu , dlatego nazywamy to wymazywaniem typu .

Łącząc te dwie rzeczy razem

Teraz problem jest kombinacja powyższych realia: jeśli List<T>staje się List<Object>we wszystkich przypadkach, po czym Tzawsze musi być typu, które mogą być bezpośrednio przypisaneObject . Nie można pozwolić na nic innego. Ponieważ, jak już powiedzieliśmy, int, floati doublenie są wymienne z Object, tam nie może być List<int>, List<float>lubList<double> (chyba znacznie bardziej skomplikowane wdrożenie generycznych istniał w JVM).

Ale Java oferty rodzaje podoba Integer, Floata Doublektóry zawijać tych prymitywów w instancji klasy, dzięki czemu skutecznie jako substytucyjne Object, a tym samym pozwalając typy generyczne pośrednio pracy z prymitywów , jak również (bo może mieć List<Integer>, List<Float>,List<Double> i tak dalej).

Proces tworzenia Integerze związku int, A Floatod A floati tak dalej, nazywany jest boks . Odwrotność nazywa się unboxing . Ponieważ konieczność zaznaczania prymitywów za każdym razem, gdy chcesz ich używać, Objectjest niewygodna, są przypadki, w których język robi to automatycznie - nazywa się to autoboxingiem .

Theodoros Chatzigiannakis
źródło
Zgodnie z twoim wyjaśnieniem potrzebujemy tych klas Integer, String, ... do implementacji typów ogólnych. Czy jest jakiś inny powód?
Bishwas Mishra
1
@BishwasMishra Mamy je, abyśmy mogli używać tych wartości jako Objectinstancji. Jednym z zastosowań tego są typy generyczne poprzez usuwanie typów.
Theodoros Chatzigiannakis
16

Automatyczny boks jest używany do konwersji prymitywne typy danych do swoich obiektów klasy otoki. Klasa Wrapper zapewnia szeroki zakres funkcji do wykonania na typach pierwotnych. Najczęstszym przykładem jest:

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

To jest potrzebne ponieważ programiści mogą łatwo pisać kod, a JVM zajmie się Boxing i Unboxing.

Automatyczny boks przydaje się również podczas pracy z typami java.util.Collection. Kiedy chcemy stworzyć Kolekcję typów pierwotnych, nie możemy bezpośrednio stworzyć Kolekcji typu prymitywnego, możemy stworzyć Kolekcję tylko obiektów. Na przykład :

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

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

Klasy opakowujące

Każdy z 8 typów pierwotnych Javy (byte, short, int, float, char, double, boolean, long) ma oddzielną skojarzoną z nimi klasę Wrapper. Te klasy Wrapper mają predefiniowane metody przeprowadzania przydatnych operacji na pierwotnych typach danych.

Korzystanie z klas opakowujących

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

Istnieje wiele przydatnych funkcji, które zapewniają klasy Wrapper. Sprawdź dokumentację java tutaj

Unboxing jest przeciwieństwem Auto Boxing, w którym konwertujemy obiekt klasy opakowania z powrotem do jego pierwotnego typu. Jest to wykonywane automatycznie przez JVM, dzięki czemu możemy użyć klas opakowujących dla pewnych operacji, a następnie przekonwertować je z powrotem na typy pierwotne, ponieważ prymitywy powodują szybsze przetwarzanie. Na przykład :

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

W przypadku kolekcji, które działają z obiektami, stosuje się tylko automatyczne rozpakowywanie. Oto jak :

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

int a = al.get(0); // returns the object of Integer . Automatically Unboxed . 
varun
źródło
4

Typy pierwotne (niebędące przedmiotami) mają uzasadnienie w wydajności.

Typy pierwotne int, boolean, doubleto bezpośrednie dane, podczas gdy Objects to odniesienia. Stąd pola (lub zmienne)

int i;
double x;
Object s;

potrzebowałaby pamięci lokalnej 4 + 8 + 8? gdzie dla obiektu przechowywane jest tylko odniesienie (adres) do pamięci.

Używając opakowań Object Integer, Doublei innych, można by wprowadzić pośrednie, odniesienie do jakiejś instancji Integer / Double w pamięci sterty.

Dlaczego boks jest potrzebny?

To kwestia względnego zakresu. W przyszłej Javie planowane jest posiadanie ArrayList<int>, znoszącej prymitywne typy.

Odpowiedź: Na razie ArrayList działa tylko dla Object, rezerwując miejsce na odniesienie do obiektu i podobnie zarządzając czyszczeniem pamięci. Stąd typy ogólne są elementami potomnymi Object. Więc jeśli ktoś chciał mieć ArrayList wartości zmiennoprzecinkowych, należało zawinąć double w obiekt Double.

Tutaj Java różni się od tradycyjnego C ++ swoimi szablonami: klasami C ++ vector<string>, vector<int> utworzyłyby dwa produkty kompilacji. Projekt Java zakładał posiadanie jednego ArrayList.class, nie wymagając dla każdego typu parametru nowego skompilowanego produktu.

Zatem bez pakowania w Object należałoby skompilować klasy dla każdego wystąpienia typu parametru. In concreto: każda kolekcja lub klasa kontenera wymagałaby wersji dla Object, int, double, boolean. Wersja dla Object obsługiwałaby wszystkie klasy potomne.

W rzeczywistości potrzeba takiej dywersyfikacji istniała już w Javie SE dla IntBuffer, CharBuffer, DoubleBuffer, ... które działają na int, char, double. Zostało to rozwiązane w hackerski sposób, generując te źródła ze wspólnego.

Joop Eggen
źródło
4

Począwszy od JDK 5, java dodał dwie ważne funkcje: autoboxing i autounboxing. AutoBoxing to proces, w którym typ pierwotny jest automatycznie hermetyzowany w równoważnym opakowaniu, gdy taki obiekt jest potrzebny. Nie musisz jawnie konstruować obiektu. Automatyczne rozpakowywanie to proces, w którym wartość hermetyzowanego obiektu jest automatycznie wyodrębniana z opakowania typu, gdy jest wymagana. Nie musisz wywoływać metody takiej jak intValue () lub doubleValue () .

Dodanie autoboxingu i auto-unboxingu znacznie upraszcza algorytmy pisania , eliminując przynętę ręcznego pakowania i rozpakowywania wartości. Pomocne jest również unikanie błędów . Jest to również bardzo ważne dla typów generycznych , które działają tylko na obiektach. Wreszcie, autoboxing ułatwia pracę z Collection Framework .

Amarildo
źródło
2

dlaczego mamy (nie) boks?

aby pisanie kodu, w którym mieszamy prymitywy i ich alternatywy obiektowe (OO), było wygodniejsze / mniej rozwlekłe.

dlaczego mamy prymitywy i ich OO alternatywy?

typy pierwotne nie są klasami (w przeciwieństwie do C #), dlatego nie są podklasami Object i nie można ich przesłonić.

mamy prymitywy, takie jak intze względu na wydajność, i Objectalternatywy, takie jak Integerkorzyści z programowania obiektowego, a także jako drobna kwestia, aby mieć dobrą lokalizację dla stałych narzędzi i metod (Integer.MAX_VALUE i Integer.toString(int)).

Korzyści OO są najłatwiej widoczne w Generics ( List<Integer>), ale nie ograniczają się do tego, na przykład:

Number getMeSome(boolean wantInt) {

    if (wantInt) {
        return Integer.MAX_VALUE;
    } else {
        return Long.MAX_VALUE;
    }
}
hoijui
źródło
1

Niektóre struktury danych mogą akceptować tylko obiekty, bez typów pierwotnych.

Przykład: klucz w HashMap.

Zobacz to pytanie po więcej: HashMap i int jako klucz

Istnieją inne dobre powody, takie jak pole „int” w bazie danych, które również może mieć wartość NULL. Int w Javie nie może mieć wartości null; odwołanie typu Integer can. Autoboxing i unboxing umożliwiają uniknięcie pisania zbędnego kodu podczas konwersji w obie strony.

Gabriel
źródło
0

Ponieważ są to różne typy i dla wygody. Wydajność jest prawdopodobnie przyczyną posiadania typów pierwotnych.

Scott Hunter
źródło
0

ArrayList nie obsługuje typów pierwotnych, obsługuje tylko klasy. ale musimy użyć typów pierwotnych, np. int, double itp.

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

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

Klasa Integer opakowuje w obiekt wartość typu pierwotnego int, więc poniższy kod jest akceptowany.

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

możemy dodać wartość metodą add (value). Aby dodać wartość typu String, powiedz „Hello” w kodzie strArrayList is just

strArrayList.add("Hello");  

i dodaj wartość int, powiedz 54 możemy napisać

intArrayList.add(54);

ale kiedy piszemy intArrayList.add (54); kompilator konwertuje do następującego wiersza

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

Ponieważ intArrayList.add (54) jest łatwy i bardziej akceptowalny ze strony użytkownika, kompilator wykonuje trudną pracę, którą jest `` intArrayList.add(Integer.valueOf(54));autoBoxing ''.

Podobnie, aby pobrać wartość, po prostu wpisujemy intArrayList.get (0) i kompilator konwertuje na <code>intArrayList.get(0).intValue();który jest autoUnboxing.

Chhalma Sultana Chhaya
źródło
0

Autoboxing: Konwersja wartości pierwotnej na obiekt odpowiedniej klasy opakowania.

Rozpakowywanie: Konwersja obiektu typu opakowania na jego odpowiednią wartość pierwotną

// 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); 

    } 
} 
Yash Patel
źródło