Usando classes Wrapper

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

O primeiro propósito é fortalecer um mecanismo para empacotar valores primitivos em um tipo objeto, de modo que seja permitido ser inclusivos em atividades reservadas a objetos, como ser retomados por métodos que tenham como valor de retorno variáveis de referência ou adicionados a conjuntos.

O outro propósito é fortalecer um conjunto de funções utilitárias para tipos primitivos. A maior parte dessas funções está relacionada a várias convenções de tipos primitivos em objetos String e vice-versa, além de tipos primitivos em objetos String para bases diferentes, como bases binárias, octais e hexadecimais.

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?

a.     a compilação falha.

b.     o valor de x é: 43

c.     o valor de x é: 44

d.     O valor de x é: 42

e.     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?

a.     0

b.     1

c.     A compilação falha.

d.     2

e.     Não é possível saber.

f.       É 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?

a.     1 1

b.     4 3

c.     2 3

d.     4 4

e.     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?

a.     É lançada uma exceção.

b.     false true

c.     true false

d.     false false

e.     true true

f.      A compilação falha.


5. Dado:

1.class Conversao {

2.  public static void main(String[] args) {

3.     Long l1 = new Long(789L);

4.     long l2 = Long.valueOf("193");

5.     Long l3 = Long.valueOf("823");

6.     long l4 = l1.longValue();

7.     Long l5 = l1.longValue();

8.     Long l6 = Long.parseLong("43");

9.     long l7 = Long.parseLong("5");


   }


}

Qual irá compilar usando Java 5, mas não compilará usando Java 1.4? (Marque todas as corretas)

a.     linha 4

b.     linha 5

c.     linha 6

d.     linha 7

e.     linha 8

f.       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

As referências a1, a3 e z apontam para o mesmo local. Então, os conteúdos e os endereços são os mesmos.


5.Resposta A,D,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