Por que usamos autoboxing e unboxing em Java?

Autoboxing é a conversão automática que o compilador Java faz entre os tipos primitivos e suas classs de wrapper de object correspondentes. Por exemplo, converter um int em um Integer, um double em um Double e assim por diante. Se a conversão for para o outro lado, isso é chamado de unboxing.

Então, por que precisamos disso e por que usamos autoboxing e unboxing em Java?

Algum contexto é necessário para entender completamente a principal razão por trás disso.

Primitivos versus classs

As variables ​​primitivas em Java contêm valores (um inteiro, um número binário de ponto flutuante de precisão dupla, etc). Como esses valores podem ter diferentes comprimentos , as variables ​​que os contêm também podem ter comprimentos diferentes (considere float versus double ).

Por outro lado, as variables ​​de class contêm referências a instâncias. Referências são normalmente implementadas como pointers (ou algo muito semelhante a pointers) em muitos idiomas. Essas coisas normalmente têm o mesmo tamanho, independentemente dos tamanhos das instâncias a que se referem ( Object , String , Integer , etc).

Esta propriedade de variables ​​de class faz com que as referências que elas contêm sejam intercambiáveis (até certo ponto). Isso nos permite fazer o que chamamos de substituição : em termos gerais, usar uma instância de um tipo específico como uma instância de outro tipo relacionado (use um String como um Object , por exemplo).

Variáveis ​​primitivas não são intercambiáveis da mesma maneira, nem entre si, nem com Object . A razão mais óbvia para isso (mas não a única razão) é a diferença de tamanho. Isso torna os tipos primitivos inconvenientes a esse respeito, mas ainda precisamos deles na linguagem (por razões que se resumem principalmente ao desempenho).

Eliminação de genéricos e tipo

Tipos genéricos são tipos com um ou mais parâmetros de tipo (o número exato é chamado de aridade genérica ). Por exemplo, a definição de tipo genérico List tem um parâmetro de tipo T , que pode ser Object (produzindo um tipo concreto List ), String ( List ), Integer ( List ) e assim por diante .

Tipos genéricos são muito mais complicados que os genéricos. Quando eles foram apresentados ao Java (após seu lançamento inicial), para evitar mudanças radicais na JVM e possivelmente quebrar a compatibilidade com binários mais antigos, os criadores do Java decidiram implementar tipos genéricos da maneira menos invasiva: todos os tipos concretos de List são, na verdade, compilados para (o equivalente binário de) List (para outros tipos, o limite pode ser algo diferente de Object , mas você obtém o ponto). As informações genéricas de parâmetro de aridade e tipo são perdidas nesse processo , e é por isso que chamamos de exclusão de tipo .

Colocando os dois juntos

Agora o problema é a combinação das realidades acima: se List se torna List em todos os casos, então T deve ser sempre um tipo que pode ser atribuído diretamente a Object . Qualquer outra coisa não pode ser permitida. Como, como dissemos anteriormente, int , float e double não são intercambiáveis ​​com Object , não pode haver uma List , List ou List (a menos que exista uma implementação significativamente mais complicada de genéricos em a JVM).

Mas Java oferece tipos como Integer , Float e Double que envolvem essas primitivas em instâncias de classs, tornando-as efetivamente substituíveis como Object , permitindo assim que tipos genéricos trabalhem indiretamente com as primitivas também (porque você pode ter List , List , List e assim por diante).

O processo de criar um Integer partir de um int , um Float de um float e assim por diante, é chamado de boxe . O reverso é chamado de unboxing . Como ter que colocar checkboxs primitivas toda vez que você quiser usá-las como Object é inconveniente, há casos em que a linguagem faz isso automaticamente – isso é chamado de autoboxing .

Auto Boxing é usado para converter tipos de dados primitivos para seus objects de class de wrapper. A class Wrapper fornece uma ampla gama de funções a serem executadas nos tipos primitivos. O exemplo mais comum é:

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

É necessário, por causa dos programadores, ser capaz de escrever código diretamente e a JVM cuidará do Boxing e Unboxing.

Auto Boxing também vem a calhar quando estamos trabalhando com os tipos java.util.Collection. Quando queremos criar uma coleção de tipos primitivos, não podemos criar diretamente uma coleção de um tipo primitivo, podemos criar apenas a coleção de objects. Por exemplo :

 ArrayList al = new ArrayList(); // not supported ArrayList al = new ArrayList(); // supported al.add(45); //auto Boxing 

Classes de invólucros

Cada um dos oito tipos primitivos do Java (byte, short, int, float, char, double, booleano, long) tem uma class Wrapper separada associada a eles. Essas classs Wrapper possuem methods predefinidos para pré-formatar operações úteis em tipos de dados primitivos.

Uso de Classes de Invólucros

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

Existem muitas funções úteis que as classs Wrapper fornecem. Confira os documentos de java aqui

Unboxing é o oposto do Auto Boxing, onde convertemos o object da class wrapper de volta ao seu tipo primitivo. Isso é feito automaticamente pela JVM, para que possamos usar as classs do wrapper para determinada operação e, em seguida, convertê-las de volta para tipos primitivos, já que as primitivas resultam em um processamento mais rápido. Por exemplo :

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

No caso de Coleções que funcionam com objects, somente o unboxing automático é usado. Veja como:

 ArrayList al = new ArrayList(); al.add(45); int a = al.get(0); // returns the object of Integer . Automatically Unboxed . 

Os tipos primitivos (não-objects) possuem justificativa na eficiência.

Os tipos primitivos int, boolean, double são dados imediatos, enquanto Object s são referências. Daí campos (ou variables)

 int i; double x; Object s; 

precisaria de memory local 4 + 8 + 8? onde para o object apenas a referência (endereço) para a memory é armazenada.

Usando os invólucros Object Integer, Double e outros, seria introduzida uma indirecção, referência a alguma Integer / Double instance na memory heap.

Por que o boxe é necessário?

Essa é uma questão de escopo relativo. Em um java futuro, planeja-se poder ter um ArrayList , levantando tipos primitivos.

Resposta: Por enquanto, um ArrayList só funciona para Object, reservando espaço para uma referência de object e gerenciando a garbage collection da mesma forma. Daí tipos genéricos são filhos de object. Então, se alguém quisesse uma ArrayList de valores de ponto flutuante, seria necessário envolver um double em um object Double.

Aqui o Java difere do C ++ tradicional com seus modelos: classs de C ++ vector, vector criaria dois produtos de compilation. O design de Java foi para ter um ArrayList.class, não precisando de cada tipo de parâmetro um novo produto compilado.

Portanto, sem o boxe, o Object one precisaria compilar classs para cada ocorrência de um tipo de parâmetro. In concreto: toda coleção ou class de container precisaria de uma versão para Object, int, double, boolean. A versão para Object manipularia todas as classs filhas.

De fato, a necessidade de tal diversificação já existia no Java SE para IntBuffer, CharBuffer, DoubleBuffer, … que operam em int, char, double. Foi resolvido de forma hacky, gerando essas fonts de um comum.

Começando com o JDK 5, o java adicionou duas funções importantes: autoboxing e autounboxing. AutoBoxing é o processo pelo qual um tipo primitivo é automaticamente encapsulado no wrapper equivalente sempre que tal object é necessário. Você não precisa construir explicitamente um object. Auto-unboxing é o processo pelo qual o valor de um object encapsulado é automaticamente extraído de um wrapper de tipo quando seu valor é necessário. Você não precisa chamar um método como intValue () ou doubleValue () .

A adição de autoboxing e auto-unboxing simplifica muito a escrita de algoritmos , eliminando a isca de encaixotar manualmente e unboxing de valores. Também é útil evitar erros . Também é muito importante para os genéricos , que só operam em objects. Por fim, o autoboxing facilita o trabalho com o Framework de collections .

por que temos (des) boxe?

para fazer código de escrita onde misturamos primitivas e suas alternativas Orientadas a Objetos (OO) mais confortáveis ​​/ menos detalhadas.

Por que temos primitivos e suas alternativas OO?

tipos primitivos não são classs (ao contrário de C #), portanto, eles não são subclasss de Object e não podem ser substituídos.

temos primitivos como int por motivos de desempenho e as alternativas Object como Integer para os benefícios da programação OO e, como um ponto secundário, para ter uma boa localização para constantes e methods de utilitários (Integer.MAX_VALUE e Integer.toString(int) ) .

Os benefícios OO são visíveis mais facilmente com Generics ( List ), mas não estão limitados a isso, por exemplo:

 Number getMeSome(boolean wantInt) { if (wantInt) { return Integer.MAX_VALUE; } else { return Long.MAX_VALUE; } } 

Porque eles são tipos diferentes e como uma conveniência. O desempenho é provavelmente o motivo de ter tipos primitivos.

Algumas estruturas de dados podem aceitar apenas objects, sem tipos primitivos.

Exemplo: a chave em um HashMap.

Veja esta pergunta para mais: HashMap e int como chave

Há outros bons motivos, como um campo “int” em um database, que também pode ser NULL. Um int em Java não pode ser nulo; uma referência inteira pode. Autoboxing e unboxing fornecem uma facilidade para evitar a escrita de código estranho nas conversões de um lado para outro.