<h2>Introdução</h2>Uma linguagem de programação, como a linguagem que as pessoas falam, vive e muda, novos fenômenos aparecem nela para tornar a linguagem mais conveniente de usar. E como sabemos, a linguagem deve expressar convenientemente os nossos pensamentos.
Assim, no Java SE 5, o mecanismo de boxing/unboxing foi introduzido. E um tutorial separado da Oracle é dedicado aos recursos desse meio de expressar pensamentos: Autoboxing e Unboxing . <h2>Boxing de empacotamento automático</h2>Vejamos um exemplo de boxe de empacotamento automático. Primeiro, vamos ver como funciona. Vamos utilizar o site compilejava.net e criar uma classe:
Este é o mesmo notório “bytecode”. Mas o que é importante para nós agora é o que vemos. Primeiro, a primitiva 8080 é colocada na pilha de execução do método e, em seguida, Integer.valueOf é executado . Esta é a “mágica” do boxe. E por dentro a magia fica assim:
Ou seja, em essência, um novo será obtido
Aparentemente, não há mágica. Tudo está dentro do Java. Simplesmente funciona “por si só”. Para nossa comodidade. <h2>Ancinho</h2>
Qualquer ferramenta, se usada incorretamente, torna-se uma arma formidável contra si mesma. E o mecanismo automático de boxe/unboxing em Java não é exceção. A primeira e óbvia comparação é através de
public class App {
public static void main(String[] args) {
Integer portNumber = 8080;
if (args.length != 0) {
portNumber = Integer.valueOf(args[0]);
}
System.out.println("Port number is: " + portNumber);
}
}
Código simples. Podemos especificar o parâmetro de entrada e alterar o valor da porta. Como vemos, porque lemos o valor da porta String
nos parâmetros e Integer
o obtemos passando por Integer.valueOf
. Portanto, somos forçados a especificá-lo não como um tipo primitivo, mas como um tipo de objeto Integer
. E aqui temos, por um lado, uma variável de objeto, e o valor padrão é um primitivo. E funciona. Mas não acreditamos em magia, não é? Vamos dar uma olhada “nos bastidores”, como dizem. Baixe o código fonte em compilejava.net clicando em “Baixar ZIP”. Depois disso, extraia o arquivo baixado em um diretório e acesse-o. Agora vamos fazer: javap -c -p App.class
onde App.class é o arquivo de classe compilado para sua classe. Veremos conteúdos como este:
Integer
ou obtido Integer
do cache (o cache nada mais é do que apenas um array de números inteiros) dependendo do valor do número. Naturalmente, Integer
não só um teve tanta sorte. Há uma lista completa de tipos primitivos relacionados e seus wrappers (classes que representam primitivos no mundo OOP). Esta lista é fornecida no final do Tutorial da Oracle: “ Autoboxing e Unboxing ”. É importante notar imediatamente que arrays feitos de primitivos não possuem um “wrapper” sem conectar nenhuma biblioteca de terceiros. Aqueles. Arrays.asList
não fará de int[]
para nós List
a partir de Integer
. <h2>Unboxing</h2>O processo inverso ao boxing é chamado de unboxing unboxing. Vejamos um exemplo de descompactação:
public class App {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Please, enter params");
return;
}
int value = Math.abs(Integer.valueOf(args[0]));
System.out.println("Absolute value is: " + value);
}
}
Math.abs
aceita apenas primitivos. O que fazer? A classe wrapper possui um método especial para este caso que retorna um primitivo. Por exemplo, este é o Integer
método intValue . Se olharmos para o bytecode, é assim:
==
. Acho que isso está claro, mas vamos analisar novamente:
public static void main(String[] args) {
Integer inCacheValue = 127;
Integer inCacheValue2 = 127;
Integer notInCache = 128; // new Integer(129)
Integer notInCache2 = 128; // new Integer(129)
System.out.println(inCacheValue == inCacheValue2); //true
System.out.println(notInCache == notInCache2); //false
}
No primeiro caso, o valor é retirado do Integer
cache de valores (veja a explicação do Boxing acima), e no segundo caso um novo objeto será criado a cada vez. Mas aqui vale a pena fazer uma reserva. Esse comportamento depende do limite superior do cache ( java.lang.Integer.IntegerCache.high ). Além disso, este limite pode mudar devido a outras configurações. Você pode ler a discussão sobre este tópico no stackoverflow: Qual é o tamanho do cache inteiro? Naturalmente, os objetos precisam ser comparados usando iguais: System.out.println(notInCache.equals(notInCache2));
O segundo problema associado ao mesmo mecanismo é o desempenho. Qualquer boxe em Java é equivalente à criação de um novo objeto. Se o número não estiver incluído nos valores do cache (ou seja, -128 a 127), um novo objeto será criado a cada vez. Se de repente o empacotamento (ou seja, encaixotamento) for realizado em loop, isso causará um grande aumento de objetos desnecessários e consumo de recursos para o trabalho do coletor de lixo. Portanto, não seja tão imprudente com isso. Um terceiro movimento, não menos doloroso, decorre do mesmo mecanismo:
public static void check(Integer value) {
if (value <= 0) {
throw new IllegalStateException("Value is too small");
}
}
Neste código, a pessoa estava claramente tentando não superar o erro. Mas não há verificação para null
. Se se tratar da entrada null
, então, em vez de um erro compreensível, obteremos um erro incompreensível NullPointerException
. Porque para efeito de comparação, o Java tentará executar value.intValue
e travar, porque... value
vai null
. <h2>Conclusão</h2>O mecanismo de boxing/unboxing permite que o programador escreva menos código e às vezes nem pense em converter de primitivos para objetos e vice-versa. Mas isso não significa que você deva esquecer como funciona. Caso contrário, você poderá cometer um erro que pode não aparecer imediatamente. Não devemos confiar em partes do sistema que não estejam completamente sob nosso controle (como o limite inteiro). Mas não se esqueça de todas as vantagens das classes wrapper (como Integer
). Freqüentemente, essas classes wrapper possuem um conjunto de métodos estáticos adicionais que tornarão sua vida melhor e seu código mais expressivo. Aqui está um exemplo de atualização:
public static void main(String[] args) {
int first = 1;
int second = 5;
System.out.println(Integer.max(first, second));
System.out.println(Character.toLowerCase('S'));
}
A conclusão correta de tudo é que não existe mágica, existe algum tipo de realização. E nem tudo será sempre o que esperamos. Por exemplo, não há empacotamento: System.out.println("The number is " + 8);
o exemplo acima será otimizado pelo compilador em uma linha. Ou seja, é como se você escrevesse “O número é 8”. E no exemplo abaixo também não haverá embalagem:
public static void main(String[] args) {
System.out.println("The number is " + Math.abs(-2));
}
Como pode ser quando println
tomamos um objeto como entrada e precisamos conectar as linhas de alguma forma. Linhas... sim, é por isso que não existe embalagem propriamente dita. Existem Integer
métodos estáticos, mas alguns deles são package
. Ou seja, não podemos usá-los, mas no próprio Java eles podem ser usados ativamente. Este é exatamente o caso aqui. O método getChars será chamado, o que cria um array de caracteres a partir do número. Novamente, sem mágica, apenas Java). Portanto, em qualquer situação pouco clara, basta observar a implementação e pelo menos algo se encaixará. #Viacheslav
GO TO FULL VERSION