Desmistificando a Certificação SCJP6 Parte VII - Parte 3/3

Wrapper, Autoboxing e aplicaremos um mini-teste de todos os assuntos do artigo.

Usando classes Wrapper

As classes wrapper são usados para 2 propósitos principais:

Existe uma classe wrapper para cada tipo primitivo. Todas as classes wrapper, com exceção de Character, fornecem 2 construtores: um que utiliza como argumento o tipo primitivo que está sendo criado e outro que usa representação String do tipo sendo construído. Veja alguns exemplos.

Integer i = new Integer(41); Integer i1 = new Integer(“52”); Float f = new Float(3.16F); Float f1 = new Float(“2,18”);

A classe Character fornece apenas um constructor que utiliza tipo char como argumento.

Character c = new Character(‘x’);

Métodos valueOf()

Os dois métodos static valueOf() fornecidos na maior parte das classes wrapper lhe permitirão outra abordagem para a criação de objetos wrapper.

package devmedia; public class Executavel{ public static void main(String args[ ]){ Integer i1 = Integer.valueOf(“101011”,2); //converte 101011 em 43 Float f1 = Float.valueOf(“2.16F” ); int i2 = Integer.valueOf(“22”); //boxing System.out.println(i1); System.out.println(f1); System.out.println(i2); Integer a = Integer.valueOf(“true”); //exception } }

parseXxx()

Os 6 métodos parseXxx(), um para cada tipo de numérico, estão altamente relacionados ao método valueOf(), que há em todas as classes wrapper numéricas.

Analise alguns exemplos:

double a3 = Double.parseDouble(“4.06”); Double a6 = Double.valueOf(“5.33”) System.out.println(a6 instanceof Double); //true long x1 = Long.parseLong(“456789”,2); long x2 = Long.valueOf(x1);

toString()

A finalidade de toString() é dar alguma representação significativa de determinado objeto. As classes wrapper contem a versão de uma instância do método toString(), não-static e sem argumentos. Este método tem como retorno uma String como valor do tipo primitivo encapsulado no objeto. Veja um exemplo:

Double double1 = new Double("5.17"); System.out.println(double1.toString());

As classes wrapper numéricas fornecem um método sobrecarregado e static toString() que utiliza o tipo numérico primitivo apropriado: Double.toString(), Long.toString() e assim por diante, retorna uma String.

String text = Double.toString(1.95);

Sendo assim, as classes Long e Integer fornecem um terceiro método toString(). Esse é static, seu primeiro argumento é do tipo primitivo e o segundo é uma base. O argumento da base informa o método para pegar o primeiro argumento, que por padrão está na base 10, e convertê-lo para a base fornecida, retornando, como resultado, uma String. Veja a seguir:

String exemplo = "resulta em "+ Long.toString(254, 16);//resulta em fe

Autoboxing

Essa novidade do Java 5 é bem revolucionária, este recurso é conhecido por vários nomes: boxing, autoboxing, auto-unboxing, unboxing.

A partir de agora, com o recurso aprimorado e novo do Java 5, você pode utilizar:

Integer i = new Integer(549); int a = i.intValue(); a++; i = new Integer(a);

Implicitamente (“por debaixo dos panos”), o compilador faz o unboxing e a reatribuição automáticamente.

Sobrecarga com ampliação, boxing e vargars

Quando uma classe possui métodos sobrecarregados, uma das tarefas do compilador é determinar qual é o método a ser utilizado. Observe o exemplo abaixo:

package devmedia; public class Sobrecarga { static void ir(double a){ System.out.println("double version."); } /** * @param args */ public static void main(String[] args) { byte bt = 2; short sh = 2; float fl = 2.0f; long lg = 2; ir(bt); ir(sh); ir(fl); ir(lg); } }

Saída:
double version.
double version.
double version.

Adicionaremos boxing ao exemplo anterior.

package devmedia; public class Sobrecarga { static void ir(Integer a){ System.out.println("Integer version."); } static void ir(Long l){ System.out.println("Long version."); } static void ir(Double d){ System.out.println("Double version."); } /** * @param args */ public static void main(String[] args) { int bt = 2; double sh = 2; long lg = 2; ir(bt); ir(sh); ir(lg); } }

Saída:
Integer version.
Double version.
Long version.

No exemplo acima, ocorre boxing para as 3 invocações. O int é convertido para Integer, double é convertido para Double e o long é convertido para Long.

Foi decido pelas especificações que a regra mais importante deveria ser que os códigos preexistentes devem continuar funcionando como antes, então, uma vez que o recurso de ampliação já exista, um método invocado por meio da ampliação não deveria ser trocado por método recentemente criado, que utiliza boxing. Baseado nessa regra, tente supor qual o resultado do seguinte código:

package devmedia; public class AddVarargs { static void ir(int i,int a){ System.out.println("int - int"); } static void ir(byte...b){ System.out.println("byte..."); } public static void main(String[] args) { byte x = 8; ir(x,x); } }

A saída é: “int - int” porque mesmo que a chamada requeira algum tipo de conversão, o compilador escolherá o estilo antigo, ao invés do novo, mantendo os códigos já existentes mais robustos.

Analisaremos agora o que ocorre quando é preciso fazer de uma conversão. Neste caso, o compilador terá que compilar e em seguida fazer autoboxing do parâmetro, para que uma correspondência seja realizada.

package devmedia; public class AumentaBoxing { static void ir(Long n){//boxing System.out.println("Long"); } public static void main(String[] args) { Byte x =7; ir(x);//Não compila } }

Isso é muito para o compilador. Entretanto, o compilador pode fazer uma operação de boxing e depois de ampliação, no sentido de criar uma correspondência entre chamada e método.

Mini-teste

  1. Dado:
    class Main { static int disparador() throws Exception{ return 44; } public static void main(String args[]){ try { int x = disparador(); } catch (Exception e) { x++; }finally{ System.out.println("X é "+x); } } }
    Qual é o resultado?
    1. A compilação falha.
    2. O valor de x é: 43
    3. O valor de x é: 44
    4. O valor de x é: 42
    5. O código é executado sem nenhuma saída.
  2. Observe:
    class Executavel { Short s = 5; Executavel ir(Executavel e){ e=null; return e; } public static void main(String args[]){ Executavel e1 = new Executavel(); Executavel e2 = new Executavel(); Executavel e3 = e1.ir(e2); e1 = null; //aqui } }
    Quando a execução chagar em //aqui, quantos objetos estarão qualificados para a coleta de lixo?
    1. 0
    2. 1
    3. A compilação falha.
    4. 2
    5. Não é possível saber.
    6. É lançada uma exceção.
  3. Dado:
    class Principal { int fazer(Long x, Long y){return 1;} int fazer(long...x){return 2;} int fazer(Integer x, Integer y){return 3;} int fazer(Number n, Number m){return 4;} public static void main(String args[]){ new Principal().ir(); } void ir(){ short s = 4; System.out.print(fazer(s,s)+" "); System.out.print(fazer(4,4)); } }
    Qual é o resultado?
    1. 1 1
    2. 4 3
    3. 2 3
    4. 4 4
    5. 3 3
  4. Dado:
    class Mistery { int x =5; public static void main(String[] args) { final Mistery a1 = new Mistery(); Mistery a2 = new Mistery(); Mistery a3 = fazerMistery(a1, a2); System.out.println((a1==a3)+" "+(a1.x==a3.x)); } static Mistery fazerMistery(Mistery x, Mistery y){ final Mistery z = x; z.x=6; return z; } }
    Qual é o resultado?
    1. É lançada uma exceção.
    2. false true
    3. true false
    4. false false
    5. true true
    6. A compilação falha.
  5. Dado:
    class Conversao { public static void main(String[] args) { Long l1 = new Long(789L); long l2 = Long.valueOf("193"); Long l3 = Long.valueOf("823"); long l4 = l1.longValue(); Long l5 = l1.longValue(); Long l6 = Long.parseLong("43"); long l7 = Long.parseLong("5"); } }
    Qual irá compilar usando Java 5, mas não compilará usando Java 1.4? (Marque todas as corretas)
    1. Linha 4
    2. Linha 5
    3. Linha 6
    4. Linha 7
    5. Linha 8
    6. Linha 9

GABARITO COMENTADO

  1. Resposta A

    Note que no local do código a variável x é declarada. Ela é declarada dentro do bloco try/catch. Assim, seu escopo fica limitado a esse bloco. Desse modo, o único local em que o x pode ser referenciado é dentro do bloco try/catch. Uma vez que se tente utilizá-lo fora do try, um erro de compilação é lançado.

  2. Resposta D

    Apenas e1 estará qualificado; porém, ele também possui uma referência Short. o objeto para o qual essa referência aponta também estará qualificado para a coleta de lixo. Sendo assim, serão 2 objetos qualificados para o garbage collector.

  3. Resposta B

    Com relação à segunda chamada, não existem dúvidas de que a saída será 3, pois a melhor opção é boxing, de int para Integer. Com relação à primeira chamada, há, primeiramente, 2 opções: ampliar de short para long e usar varargs, ou fazer boxing de short para Short e em seguida ampliar para Number. A segunda opção é escolhida. Então, boxing e ampliação são possíveis, mas a recíproca não é verdadeira.

  4. Resposta E

    Com relação à segunda chamada, não existem dúvidas de que a saída será 3, pois a melhor opção é boxing, de int para Integer. Com relação à primeira chamada, há, primeiramente, 2 opções: ampliar de short para long e usar varargs, ou fazer boxing de short para Short e em seguida ampliar para Number. A segunda opção é escolhida. Então, boxing e ampliação são possíveis, mas a recíproca não é verdadeira.

  5. Resposta E

    O método valueOf() tem como retorno uma referência. Os métodos longValue() e parseLong() retornam primitivos. Só não existe necessidade de conversão quando houver paridade entre o tipo de retorno e a atribuição. A partir do Java 5 a conversão é implicita e é chamada de autoboxing.

Espero que possam aproveitar esse artigo, bons estudos e até a próxima!


Leia todos artigos da série

Artigos relacionados