Se você já trabalhou com Java, mesmo que por pouco tempo já deve ter trabalhado alguma vez com Wrappers e nem percebeu. Este artigo tem como principal objetivo mostrar a importância dos Wrappers e principalmente o que eles são.

Wrappers vem do verbo inglês “wrap” que significa envolver. Eles são um nome adicional ao padrão de projeto Decorator. Tem como principal função “envolver coisas” adicionando funcionalidades à ela.

O Java conta com diversos Wrappers que adicionam funcionalidades a outras classes ou tipos primitivos, um exemplo dele é o Integer, que é o Wrapper do tipo primitivo int. Que tipo de funcionalidade você pode executar tendo apenas uma variável definida como “int”? Nenhuma, a não ser atribuir valores, pois int é um tipo primitivo e não possuí métodos.

O Java possui oito wrappers para tipos primitivos que adicionam a funcionalidade de tratar tipos primitivos como classes. Quando você faz um:

Integer i = Integer.valueOf(2); 
Você está criando uma classe, que "envolve" o número 2 (primitivo) e adiciona métodos como intValue() nele. Você ainda ganha a funcionalidade de trabalhar com o número 2 como se ele fosse um objeto. O Java (a partir da versão 5) é inteligente o suficiente para criar ou desfazer wrappers de tipo primitivo automaticamente (Autoboxing), de tão útil e comum que é essa prática, veremos mais a diante como funciona o Autoboxing.

Existem dezenas de wrappers: para tratar o fluxo de conexões como orientado a objetos (ObjectInputStream), fluxos de audio (AudioInputStream), baseados em tipos primitivos (DataInputStream), ou adicionar buffers (BufferedInputStream) a eles. Todos esses são exemplos de wrappers para o básico e sem funcionalidade InputStream. E o interessante, é que, esses wrappers podem ser combinados (pode-se fazer um FileInputStream, associa-lo a um BufferedInputStream e ler dados primitivos dele após associar o resultado a um DataInputStream).

Com o Wrapper Integer envolvendo o tipo primitivo int você consegue executar métodos como é o caso do: parseInt, valueOf e assim por diante.

Na Figura 1você confere uma lista de Wrappers muito comuns no Java.

Figura 1. Wrappers de Tipos primitivos

Então se temos Wrappers porque usar os tipos primitivos? Simples, eles são mais rápidos e consomem menos memória, afinal não tem implementação de métodos ou qualquer outro algoritmo complexo que venha a consumir mais tempo da JVM.

Fique atento ao tentar realizar conversões de para Wrappers do tipo errado, pois você verá exceções como “NumberFormatException”, como por exemplo a tentativa de converter o literal “dois” para um tipo “Integer”. A atenção deve ser dobrada pois não há erro em tempo de compilação, ou seja, seu código não mostrará nenhum erro de sintaxe, mas quando esta linha de código for executada você verá a exceção apontada acima. Veja o exemplo da Listagem 1.

Listagem 1. Convertendo de forma errada


  public class ExceWrap {
    public static void main(String[] args) {
      int z= Integer.parseInt("dois");
      System.out.println(z);
      /* codigo compila sem problemas mais uma excecao vai acontecer
       * nao há nada de errado na sintaxe
       * porem a jvm nao consegue converter uma string literal
       * em um numero inteiro
       */
   }}
   Exception in thread "main" java.lang.NumberFormatException: For input string: "dois"
   
  //Por outro lado, a classe abaixo irá compilar e executar normalmente
  public class Par {
    public static void main(String[] args) {
       //String - primitivo
       double d = Double.parseDouble("10");
       System.out.println(d);
  }}
   
  //A classe abaixo também apresentará erro
   
  public class NoValue {
   public static void main(String[] args) {
      Long l = new Long(8);
      int lh = l.longValue();
      /* nao compila o to tentando colocar um long
       * dentro de um int nao é valido isso
       */
   }}
   
  //Esta funcionará normalmente
  public class OfVal {
   public static void main(String[] args) {
      //String --> OBJETOS
      Float f = Float.valueOf("10");
      System.out.println(f);
      Integer i = new Integer(10);
      //Objeto --> Primitivo
      int g = i.intValue();
    }}
   
    //Veja um exemplo de teste de referência
    public class WrpObjt {
      public static void main(String[] args) {
        Integer g = new Integer (10);
        Integer h = new Integer(10);
        System.out.println(g==h);//false
         /* aqui dois objetos diferentes
          * porem com mesmo valor
          * == testa a referencia e nao valores dos 
          * objetos */}}  

Atente também que tipos primitivos não podem ser utilizados em Collections, só objetos. Sendo assim, a solução é usar Wrappers. E já que estamos no assunto de Wrappers, não poderíamos falar da novidade trazida pelo Java 5, o Autoboxing que será explicado na próxima seção.

Autoboxing com Wrappers

Você já deve ter se imagine que como Wrapper é um Objeto você deve sempre passar um tipo primitivo para criar um Wrapper especifico daquele tipo primitivo, algo como mostrado na Listagem 2.

Listagem 2. Criando um Wrapper


  Integer soma = new Integer(10);  

Ótimo, e se quisermos criar um Hashmap teríamos que fazer algo como mostrado na Listagem 3.

Listagem 3. Criando uma lista sem Autoboxing


  HashMap hashMap = new HashMap();
  hashMap.put(new Integer(10), “Carlos”);
  hashMap.put(new Integer(11), “Jose”);
  hashMap.put(new Integer(12), “Pedro”);  

Após o Java 5 surgiu a funcionalidade do Autoboxing onde o próprio Java já converte o tipo primitivo em Wrapper se este achar que é necessário. Veja a Listagem 4 com Autoboxing funcionando.

Listagem 4. Criando uma lista com Autoboxing


  HashMap hashMap = new HashMap();
  hashMap.put(10, “Carlos”);
  hashMap.put(11, “Jose”);
  hashMap.put(12, “Pedro”);  

Boxing Conversion

O Boxing é a conversão de tipos primitivos em seu respectivo Wrapper correspondente, ou seja, veja o exemplo na Listagem 5.

Listagem 5. Boxing Conversion


  Boolean meuBoolean = true; 
  //a conversão é feita de forma automática para o boolean
  Integer meuInteger = 1203; 
  Double meuDouble = 10.20;  

É óbvio que se você tentar realizar um Boxing Conversion de um tipo primitivo para um Wrapper errado você terá um erro de compilação.

Unboxing Conversion

O Unboxing Conversion é quando você deseja fazer o inverso feito na seção 2.1, ou seja, deseja converter um objeto para um tipo primitivo. Veja os exemplos na Listagem 6.

Listagem 6. Exemplos de Unboxing Conversion


  boolean a = new Boolean(“True”);
  char c = new Character(‘c’);
  byte b = new Byte(“1”);
  float f = new Float(1.0f);   

Comparando Wrappers

Possivelmente você já deve ter comparado Strings em Java, e deve saber que utilizar o operador “==” não funciona, é por isso que a implementação no método equals nos “Pojos” são muito importantes. Com os Wrappers você também deve utilizar o método equals para realizar estes tipos de comparações, não esqueça que Wrappers são objetos e não tipos primitivos.

Veja na Listagem 7 uma comparação certa e outra errada.

Listagem 7. Comparando Wrappers da forma certa e da forma errada


  Integer a = 200;
  Integer b = 200;
  a.equals(b); // Retorna TRUE
  a == b; // Retorna FALSE  

A diferença entre o equals e o operador ==, é que no operador == ele compara a referência do objeto, afinal você está literalmente “comparando” os objetos e mesmo que estes fossem idênticos (em todos os atributos) eles ocupam locais diferentes na memória e possuem identificação diferente na JVM, resumindo não são iguais. Utilizando o equals você compara o valor deles, igual como se tivesse comparando dois tipos primitivos.

O Problema encontrado

Quando falamos que você não deve utilizar o == para comparar Wrappers, é porque não deve mesmo e continuamos afirmando isso. Ainda afirmamos que quando você realizar esse tipo de comparação receberá FALSE, visto que ele compara a referência em memória do objeto. O problema é que se você fizer o teste da Listagem 8 verá que ele retornará verdadeiro.

Listagem 8. Comparando Wrappers com o operador ==


  Integer x = 120;
  Integer y = 120;
  x==y; //Retorna TRUE  

Porque então o exemplo da Listagem 8 retorna TRUE? Por causa do padrão de projeto Flyweigth. Ele tenta reduzir o consumo de memória mudando a referência dos objetos que são idênticos, ou seja, os objetos x e y apontam para um mesmo objeto com que tem as características ditando que o valor é 120, assim quando compararmos com ==, já que estamos comparando a referência do objeto e ela é a mesma com o Flyweigth o resultado será TRUE. Isso por padrão só é aplicado nos valores de -128 até 127, mas na dúvida use sempre o equals.

Possivelmente, como citamos no inicio do artigo, você já deve ter usado Wrapper em algum momento, mas nunca soube de fato o que era, na verdade isso acaba sendo tão automático que muitas das vezes você deve ter usado o Integer, Float ou Boolean Wrapper pensado que é um tipo primitivo, quando na verdade você está envolvido pela Orientação à Objetos.

A importância deste nem precisa ser descrita, já que são utilizados com freqüência e fazem a maior parte do trabalho quando precisamos. Além disso, existem mais Wrappers do que apenas de tipos primitivos, faça uma pesquisa sobre estes e encontrará um mundo de novos conceitos e aprendizados.

Wrappers não são utilizados apenas com tipos primitivos, pelo seu conceito de “envolver coisas”, podemos utilizá-los em objetos, por exemplo: Imagine que você quer adicionar mais funcionalidades a um Jbutton padrão, então você poderia criar um Wrapper chamado WrapperJButton que conteria o Jbutton e mais vários método que você acha necessário para sua aplicação.