Introdução: por que todo desenvolvedor Java deve conhecer este truque
Imagine: você recebeu um JSON do servidor, com dezenas de objetos do mesmo tipo. Clientes, produtos, logs — não importa. Você pega o ObjectMapper, escreve readValue(json, MyClass[].class) — e tudo funciona. Mas um mês depois o código falha com ClassCastException, porque alguém substituiu o array por um List. Familiar?
A pergunta «How to use Jackson to deserialise an array of objects» no Stack Overflow acumulou dezenas de milhares de visualizações. E não é por acaso: desserialização de array de objetos em Java é uma daquelas tarefas que parecem simples até você enfrentar genéricos e Type Erasure. Em 2025, Jackson continua sendo o padrão de facto para trabalhar com JSON, mas as melhores práticas mudaram. Neste artigo, abordaremos não apenas as abordagens básicas, mas também técnicas modernas que o livrarão de dores de cabeça.
Abordagem básica: array ou lista?
A maneira mais simples é usar um array. Jackson entende perfeitamente que MyClass[].class é um array de objetos:
ObjectMapper mapper = new ObjectMapper();String json = "[{\\"id\\":1,\\"name\\":\\"Alice\\"},{\\"id\\":2,\\"name\\":\\"Bob\\"}]";MyClass[] array = mapper.readValue(json, MyClass[].class);Funciona? Sim. Mas esta abordagem tem desvantagens:
- Array de tamanho fixo — inconveniente para dados dinâmicos.
- Não possui métodos de coleções (
stream(),filter(),remove()). - Mais difícil de expandir se posteriormente precisar de
List.
Portanto, em 90% dos casos, você vai querer obter List<MyClass>. E é aqui que começa a parte interessante — Type Erasure em Java. Se você escrever:
List<MyClass> list = mapper.readValue(json, List.class); // warning: uncheckedO compilador reclamará e, em tempo de execução, você obterá um List<LinkedHashMap>, não um List<MyClass>. Os genéricos são apagados e o Jackson não sabe em que mapear os elementos.
TypeReference: a maneira correta para List
A solução é usar TypeReference. Esta é uma classe interna anônima que preserva a informação sobre os genéricos através de reflexão:
List<MyClass> list = mapper.readValue(json, new TypeReference<List<MyClass>>() {});Observe o {} — isso cria uma classe anônima que captura o tipo. O Jackson, através de TypeFactory, entende que precisa desserializar um List de MyClass. Esta abordagem é o padrão ouro em 2025.
Quando o TypeReference é inconveniente?
- Se você estiver formando o tipo dinamicamente (por exemplo, a partir de configuração).
- Se precisar desserializar o mesmo tipo muitas vezes — criar uma classe anônima toda vez é um pouco custoso.
Para esses casos, existe uma alternativa — CollectionType.
CollectionType: o poder do JavaType
O Jackson fornece TypeFactory para construir tipos complexos:
JavaType type = mapper.getTypeFactory() .constructCollectionType(List.class, MyClass.class);List<MyClass> list = mapper.readValue(json, type);Este método:
- Não cria classes anônimas (menos overhead).
- Permite construir tipos dinamicamente:
constructParametricType(Map.class, String.class, MyClass.class). - Ideal para bibliotecas e frameworks onde os tipos são desconhecidos em tempo de compilação.
Melhor prática de 2025: use TypeReference para casos simples, CollectionType quando precisar de flexibilidade ou desempenho.
Reader e InputStream: não se esqueça dos recursos
Blogs
Book Recommendations
Leia também
Tratamento de Erros em Go: try/catch e Além — Nível Avançado
CI/CD para projetos Rust: GitHub Actions nível avançado
Funções Serverless em Java: AWS Lambda Nível Avançado
String para Int em Swift: 5 métodos com benchmarks e otimização
Trabalho com datas e horas em JavaScript: métodos e bibliotecas avançados