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:
- Números inteiros:
byte,short,int,long - Números de ponto flutuante:
float,double - Caractere:
char - Valor lógico:
boolean
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.
| Tipo | Tamanho | Valor Padrão |
|---|---|---|
| byte | 1 byte | 0 |
| short | 2 bytes | 0 |
| int | 4 bytes | 0 |
| long | 8 bytes | 0L |
| float | 4 bytes | 0.0f |
| double | 8 bytes | 0.0d |
| char | 2 bytes | ’\u0000’ |
| boolean | ~1 byte | false |
⚠️ 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:
- Função: bloco de código que executa uma tarefa específica (conceito geral, presente em várias linguagens).
- Método: função declarada dentro de uma classe, associada a um comportamento daquela classe.
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 Primitivo | Classe Wrapper |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
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 anew 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:
- Preciso que o valor possa ser
null? - Preciso usar esse valor em coleções (
ArrayList,HashMap,HashSet)? - Preciso de métodos utilitários como
Integer.parseInt,Integer.MAX_VALUE,Boolean.parseBoolean,Character.isDigit? - Preciso passar esse valor para uma API que trabalha com objetos (Generics, Reflection, etc.)?
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:
- Use primitivos sempre que possível, por simplicidade e performance.
- Use wrappers quando precisar de
null, de inserir valores em coleções, ou de usar os métodos utilitários da classe. - Cuidado com o unboxing de
null— uma das causas mais comuns de NPE em Java. - Para comparar wrappers, prefira
.equals()em vez de==.
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! 🚀