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