← início

Tipos Primitivos & Wrappers

Fala pessoal, beleza? Me deparei com este conceito durante esta semana e decidi escrever um artigo sobre o tema, quem sabe ajuda alguém no dia de amanhã, não é mesmo? Então vamos lá.

Tipos Primitivos e Wrappers

De forma direta: os tipos primitivos são o tipo mais básico da linguagem Java. Eles não possuem métodos, ocupam pouco espaço em memória e geralmente são usados como variáveis locais, quando não precisamos manipular seus valores de forma mais elaborada.

Já os wrappers (“embrulhos”) encapsulam um primitivo, transformando-o em um objeto. Com isso ganhamos métodos de instância, métodos utilitários estáticos da classe, e a possibilidade de usar esses valores em lugares onde só objetos são aceitos (como coleções e generics).

Tipos Primitivos

Em Java temos os seguintes primitivos:

Eles têm tamanho fixo em memória, possuem valor padrão (quando usados como campos de uma classe) e não possuem métodos.

TipoTamanhoValor Padrão
byte1 byte0
short2 bytes0
int4 bytes0
long8 bytes0L
float4 bytes0.0f
double8 bytes0.0d
char2 bytes’\u0000’
boolean~1 bytefalse

⚠️ Detalhe importante: valores padrão só existem para campos de classe/instância. Para variáveis locais, o Java exige inicialização explícita — caso contrário, dá erro de compilação.

Onde utilizamos primitivos?

Geralmente em exercícios mais simples de lógica, em variáveis locais dentro de métodos, em cálculos numéricos e em qualquer cenário onde performance importa, já que primitivos são mais rápidos e não geram alocação no heap.

Diferença entre função e método

Já que toquei nesse ponto, uma explicação rápida:

Vale lembrar que em Java não existe “função solta” — tudo vive dentro de uma classe. O mais próximo de “funções” são as lambdas e a interface Function<T,R>, mas isso é assunto para outro artigo.

Wrappers

Wrapper é literalmente um “embrulho”: pegamos um primitivo e o representamos como um objeto. Com isso ganhamos métodos de instância e métodos utilitários estáticos da classe correspondente.

Importante: o wrapper não modifica o primitivo — ele é um outro objeto que carrega aquele valor. Além disso, wrappers são imutáveis: uma vez criados, seu valor interno não muda.

Tipo PrimitivoClasse Wrapper
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Um exemplo prático desses “super-poderes”: imagine que você recebeu um número como String e precisa convertê-lo para int. Com o wrapper Integer, isso é simples:

String s = "42";
int numero = Integer.parseInt(s); // método utilitário da classe Integer
System.out.println(numero + 8); // saída: 50

Autoboxing e Unboxing

Antes do Java 5, a conversão de primitivo para wrapper se chamava boxing, e a conversão inversa, unboxing. A partir do Java 5, o compilador passou a fazer esse processo automaticamente — daí os nomes autoboxing e unboxing.

// Antes do Java 5 — forma manual (boxing)
int x = 10;
Integer box = new Integer(x); // após o Java 9, o construtor foi marcado como @Deprecated

// Forma recomendada hoje
int y = 10;
Integer box2 = Integer.valueOf(y); // usa cache interno para valores entre -128 e 127
// A partir do Java 5
Integer box = 10; // autoboxing
int x = box;      // unboxing

💡 Integer.valueOf() é preferível a new Integer() porque utiliza um cache interno para valores de -128 a 127, evitando criar novos objetos a cada chamada.

⚠️ Cuidado: NullPointerException no unboxing

Como wrapper é um objeto, ele pode ser null. Quando o compilador faz unboxing automático de um valor nulo, estoura NPE:

Integer x = null;
int y = x; // NullPointerException!

Esse é um dos bugs mais comuns em Java. Sempre que misturar wrappers e primitivos, fique atento à possibilidade de null.

⚠️ Cuidado: == vs .equals()

Integer a = 127, b = 127;
Integer c = 128, d = 128;

System.out.println(a == b); // true  -> mesmo objeto (cache de -128 a 127)
System.out.println(c == d); // false -> objetos diferentes
System.out.println(c.equals(d)); // true -> comparação por valor

Para comparar valores de wrappers, sempre use .equals(). O == compara referências.

Por que usar Wrappers?

Algumas perguntas que ajudam a decidir:

O motivo mais importante: uso em Coleções e Generics

As estruturas de dados do Java (ArrayList, HashSet, HashMap, etc.) trabalham apenas com objetos — generics não aceitam primitivos. Por isso List<int> não existe, apenas List<Integer>.

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(null); // permitido: wrapper aceita null
    list.add(3);

    System.out.println("Lista: " + list);
}

Note que conseguimos inserir null na lista — algo impossível com primitivos.

E quanto à performance?

Wrappers têm um custo: cada um é um objeto no heap, o que aumenta o uso de memória e a pressão sobre o Garbage Collector. Em código com grandes volumes de dados, prefira primitivos — ou use as APIs especializadas como IntStream, LongStream e DoubleStream, que evitam o autoboxing.

Conclusão

Resumindo:

Sobre os métodos (estáticos e de instância), recomendo praticar e explorar os mais usados no dia a dia: parseInt, valueOf, toString, compareTo, MAX_VALUE, MIN_VALUE, entre outros.

Até a próxima! 🚀