JavaClasses Invólucro (Wrapper Classes)

Classes Invólucro (Wrapper Classes)

Classes Invólucro (Wrapper Classes) em Java

Por que elas existem?

Imagine que você está montando um quebra-cabeça. Algumas peças são simples, outras são mais complexas. Em Java, temos dois tipos de dados:

Tipos primitivos: são os "peões" do Java - simples, rápidos, mas limitados. Exemplos: int, double, boolean, char.

Objetos: são os "reis" do Java - poderosos, cheios de métodos, mas um pouco mais pesados.

O problema? Em algumas situações, o Java só aceita objetos. É como se você precisasse de uma peça especial, mas só tem peças simples. As Classes Invólucro (Wrapper Classes) são a solução! Elas "embrulham" um valor primitivo dentro de um objeto.

Tabela: Primitivo vs Wrapper

PrimitivoWrapperExemplo de criação
intIntegerInteger idade = new Integer(25);
doubleDoubleDouble altura = new Double(1.75);
booleanBooleanBoolean ativo = new Boolean(true);
charCharacterCharacter letra = new Character('A');
byteByteByte b = new Byte((byte) 10);
shortShortShort s = new Short((short) 100);
longLongLong l = new Long(1000L);
floatFloatFloat f = new Float(3.14f);

Dica: A partir do Java 9, os construtores públicos de Wrapper foram depreciados. Prefira usar Integer.valueOf(25) ou autoboxing.

Autoboxing e Unboxing Automático

Desde o Java 5, você não precisa mais criar manualmente objetos Wrapper. O Java faz isso automaticamente!

Autoboxing: primitivo → Wrapper (automático) Unboxing: Wrapper → primitivo (automático)

// Antes do Java 5 (manual)
Integer idade = Integer.valueOf(25);
int valor = idade.intValue();

// Depois do Java 5 (automático)
Integer idade = 25;  // autoboxing
int valor = idade;   // unboxing

// Exemplo prático
Integer numero1 = 10;
Integer numero2 = 20;
Integer soma = numero1 + numero2;  // unboxing, soma, autoboxing
System.out.println(soma);  // 30

Métodos Úteis das Wrapper Classes

Convertendo String para número

// Integer.parseInt() - retorna int primitivo
String strIdade = "25";
int idade = Integer.parseInt(strIdade);
System.out.println(idade + 5);  // 30

// Double.parseDouble() - retorna double primitivo
String strAltura = "1.75";
double altura = Double.parseDouble(strAltura);
System.out.println(altura + 0.1);  // 1.85

// Boolean.parseBoolean() - retorna boolean primitivo
String strAtivo = "true";
boolean ativo = Boolean.parseBoolean(strAtivo);

// Character não tem parse, mas tem métodos úteis
char letra = 'A';
System.out.println(Character.isDigit(letra));  // false
System.out.println(Character.isLetter(letra)); // true
System.out.println(Character.toLowerCase(letra)); // 'a'

Constantes úteis

System.out.println("Maior valor int: " + Integer.MAX_VALUE);  // 2147483647
System.out.println("Menor valor int: " + Integer.MIN_VALUE);  // -2147483648
System.out.println("Maior valor double: " + Double.MAX_VALUE);  // 1.7976931348623157E308

Convertendo para binário, hexadecimal, etc.

int numero = 42;

System.out.println(Integer.toBinaryString(numero));  // 101010
System.out.println(Integer.toHexString(numero));     // 2a
System.out.println(Integer.toOctalString(numero));   // 52

// O contrário também funciona
int deVolta = Integer.parseInt("101010", 2);  // 42
System.out.println(deVolta);  // 42

Uso com ArrayList (e outras coleções)

ArrayList não aceita tipos primitivos! É aqui que as Wrapper Classes brilham:

import java.util.ArrayList;

// ERRADO: ArrayList<int> numeros = new ArrayList<>();

// CERTO:
ArrayList<Integer> idades = new ArrayList<>();

idades.add(25);    // autoboxing: int → Integer
idades.add(30);
idades.add(18);

// Acessando valores
int primeiraIdade = idades.get(0);  // unboxing: Integer → int
System.out.println("Primeira idade: " + primeiraIdade);

// Iterando
for (int idade : idades) {  // unboxing automático
    System.out.println(idade);
}

Comparação com equals()

Aqui mora um perigo! Comparar Wrapper com == pode dar resultados inesperados:

Integer a = 127;
Integer b = 127;
System.out.println(a == b);      // true (cuidado! funciona por causa do cache)
System.out.println(a.equals(b)); // true (forma correta)

Integer c = 128;
Integer d = 128;
System.out.println(c == d);      // false! (objetos diferentes)
System.out.println(c.equals(d)); // true (comparação correta de valores)

// Regra de ouro: SEMPRE use equals() para comparar Wrapper
Integer x = new Integer(100);
Integer y = new Integer(100);
System.out.println(x == y);      // false (objetos diferentes)
System.out.println(x.equals(y)); // true

Por que 127 funciona e 128 não? O Java faz cache de Integer entre -128 e 127. Fora desse intervalo, cria novos objetos.

Erros Comuns

1. NullPointerException com Wrapper null

Integer numero = null;
int valor = numero;  // NullPointerException! Unboxing de null

// Como evitar:
if (numero != null) {
    int valor = numero;
} else {
    System.out.println("Número é null");
}

// Ou use Optional (Java 8+)
Optional<Integer> opt = Optional.ofNullable(numero);
int valor = opt.orElse(0);  // valor padrão se null

2. Confundir parseInt com valueOf

String str = "123";

// parseInt() retorna int primitivo
int primitivo = Integer.parseInt(str);

// valueOf() retorna Integer (objeto)
Integer objeto = Integer.valueOf(str);

// Ambos funcionam, mas cuidado com o tipo de retorno!
ArrayList<Integer> lista = new ArrayList<>();
lista.add(Integer.valueOf("10"));  // certo
// lista.add(Integer.parseInt("10"));  // erro! não aceita int primitivo

3. Esquecer de tratar NumberFormatException

String numeroInvalido = "abc";
try {
    int valor = Integer.parseInt(numeroInvalido);  // lança NumberFormatException
} catch (NumberFormatException e) {
    System.out.println("Formato inválido: " + e.getMessage());
}

4. Comparar boolean com ==

Boolean b1 = true;
Boolean b2 = true;
System.out.println(b1 == b2);      // true (cache funciona)
System.out.println(b1.equals(b2)); // true

Boolean b3 = new Boolean(true);
Boolean b4 = new Boolean(true);
System.out.println(b3 == b4);      // false! objetos diferentes
System.out.println(b3.equals(b4)); // true

Resumo Prático

public class ExemploCompleto {
    public static void main(String[] args) {
        // 1. Criando Wrapper
        Integer idade = 25;  // autoboxing
        
        // 2. Convertendo String
        String strIdade = "30";
        int idadePrimitivo = Integer.parseInt(strIdade);
        Integer idadeObjeto = Integer.valueOf(strIdade);
        
        // 3. Usando em ArrayList
        ArrayList<Integer> numeros = new ArrayList<>();
        numeros.add(10);
        numeros.add(20);
        
        // 4. Métodos úteis
        System.out.println(Integer.MAX_VALUE);
        System.out.println(Integer.toBinaryString(42));
        
        // 5. Comparação correta
        Integer a = 1000;
        Integer b = 1000;
        System.out.println(a.equals(b));  // true
        
        // 6. Cuidado com null
        Integer nulo = null;
        // int valor = nulo;  // NullPointerException!
        if (nulo != null) {
            int valor = nulo;
        }
    }
}

Dicas Finais

  1. Prefira autoboxing para criar Wrapper: Integer x = 10;
  2. Use equals() para comparar Wrapper, nunca ==
  3. Sempre verifique null antes de fazer unboxing
  4. Use parseInt() quando precisar de primitivo, valueOf() quando precisar de objeto
  5. Trate NumberFormatException ao converter strings
  6. Wrapper são imutáveis: uma vez criados, não mudam de valor

Lembre-se: as Wrapper Classes são suas aliadas para trabalhar com coleções e APIs que exigem objetos. Pratique com exemplos reais e logo você estará usando-as naturalmente!