13 março 2009

SCJP - 03 - Wrappers e Autoboxing

Talvez seja nesse item que eu tenha visto a pior pegadinha em java até agora... Eu juro que se uma questão dessa me fosse feita, eu iria errar feio. Onde ja se viu uma coisa dessas?




Bem, vamos fazer uma breve introdução (lá ele) sobre o assunto. Os Wrappers são aquelas classes com nomes de primitivas em java: Integer, Boolean, Float, Long, Double, Short, Byte, Character (e não Char). Elas são usadas pra envolver as primitivas, e utiliza-las em lugares onde só se manipulam objetos, como coleções, por exemplo. Você pode ter um Integer como chave, num par chave-valor comumente utilizado num HashMap<Integer,Object> da vida.

Essas classes possuem uma série de métodos utilitários, vistos rapidamente a seguir:

  • valueOf: Existem normalmente 3 métodos com essa assinatura, todos eles estáticos. A função desse método é devolver uma referência a um objeto, cujo valor é o passado por parâmetro:

    Integer i1 = Integer.valueOf(10);
    Integer i2 = Integer.valueOf("10");
    Integer i3 = Integer.valueOf("1000",7);
    

    A primeira e a segunda linha são autoexplicativas, mas a terceira implica em dizer que a string "1000" vai ser interpretada na base 7, e não na base decimal (padrão, como na segunda linha).

    Uma exceção para os tipos não numéricos (Boolean e Character): Boolean não precisa de "base", e Character não tem como ser analisada a partir de uma String.

  • parseXxx: Para cada wrapper numérico, existe um método que dá uma instância do objeto em questão, cujo valor é obeito analisando a String passada por parâmetro. Ex.:

    Double double = Double.parseDouble("3.1417f");
    

  • toString: Mais que intuitivo. Mas, também tem 3 versões, duas delas estáticas:

    Integer i = new Integer(10);
    
    System.out.println(i.toString());
    System.out.println(Integer.toString(10));
    System.out.println(Integer.toString(12345,6);
    

    Novamente, a última linha indica que o valor 12345 vai ser exibido na base 6.

  • toXxxString(): Existem 3 métodos na maioria das classes wrapper, que retornam uma string representando o valor primitivo em questão transformado para a base descrita na assinatura do método. São elas toBinaryString(), toOctalString() e toHexString(). O engraçado é que Double tem toHexString(), mas não possui as outras duas. Character não possui nenhuma das três.

Bem, pra começar a falar de autoboxing, olhe ai embaixo:

Integer i = 10;

Viu? Sério? Poha man, mangueie não... A variável i não é primitiva. É uma referência a um objeto... E você a inicializou com um literal... E ai? Viu agora? :D Então... A JMV automaticamente "encapsulou" o literal 10 dentro de um objeto do tipo Integer pra você (boxing).

Integer i = 10;
i++;
System.out.println(i);

Viu de novo? Sério? Tá de sacanagem né? Você não viu lá o operador de pós-incremento usado numa variável de referência? Então, a JMV "desencapsula" (unboxing) o objeto inteiro, aplica o operador de pos-incremento na primitiva resultante, depois "reencapsula" (boxing) de novo e reatribui o novo objeto à variável de referência. O que? Eu não te contei? Ops, foi mal... Assim como as Strings que nós vimos em algum outro post, todos os wrappers são imutáveis. Se você altera o valor de um deles, um novo objeto é criado na heap. Se você não reatribui o novo objeto gerado à sua variável de referência, a alteração que você fez será perdida.

E agora, finalmente, a sacanagem.

Integer i1 = 1234;
Integer i2 = 1234;

if (i1 != i2) {
    System.out.println("objetos diferentes");
}

if (i1 == i2) {
    System.out.println("objetos iguais");
}

if (i1.equals(i2)) {
    System.out.println("objetos com o mesmo conteudo");
}

Por onde vocês acham que vai passar? Certo quem disse que passa pelo != e não pelo ==. Agora altere ambos os valores para 123. E agora? Acertou quem disse que passa pelo == e não pelo !=. Mas por que? Pegadinha dos sacanas da Sun?

A fim de economizar memória, duas instâncias do mesmo tipo e com o mesmo valor primitivo, para os seguintes wrappers (criados através de boxing), serão sempre == :

  • Boolean

  • Byte

  • Character entre \u0000 e \u007f (007f é 127 em decimal)

  • Short e Integer, entre -128 e 127


Agora me digam, QUEM vai se apegar a um detalhe desses? É ou não é uma pegadinha bem sacana? E mais uma coisa: não vá pensando que os wrappers são inicializados que nem as primitivas: o valor padrão deles é null também.



Creative Commons License
Esta obra está licenciada sob uma Licença Creative Commons.
Comentários
1 Comentários

1 comments:

Mônica Paz disse...

Muito lega essa sua caricatura rsrsrs beijos

Postar um comentário

Regras são chatas, mas...

- Seu comentário precisa ter relação com o assunto do post;
- Em hipótese alguma faça propaganda de outros blogs ou sites;
- Não inclua links desnecessários no conteúdo do seu comentário;
- Se quiser deixar sua URL, comente usando a opção OpenID;
- CAIXA ALTA, miguxês ou erros de ortografia não serão tolerados;
- Ofensas pessoais, ameaças e xingamentos não são permitidos;