---
title: "Data Classes em Kotlin Tutorial em Português — Passo a Passo | Kotlin Brasil"
url: "https://kotlin.dev.br/tutoriais/data-classes-tutorial/"
markdown_url: "https://kotlin.dev.br/tutoriais/data-classes-tutorial.MD"
description: "Aprenda Data Classes em Kotlin com exemplos práticos: sintaxe, métodos gerados, destructuring, copy e comparação com classes regulares neste tutorial."
date: "2025-06-24"
author: "Karina Melo"
---

# Data Classes em Kotlin Tutorial em Português — Passo a Passo | Kotlin Brasil

Aprenda Data Classes em Kotlin com exemplos práticos: sintaxe, métodos gerados, destructuring, copy e comparação com classes regulares neste tutorial.


Neste tutorial, você vai aprender tudo sobre **Data Classes em Kotlin** — um recurso que elimina toneladas de código boilerplate que você precisaria escrever em Java. Data classes geram automaticamente métodos como `equals()`, `hashCode()`, `toString()`, `copy()` e funções `componentN()` para [destructuring](/glossario/destructuring/). Ao final deste guia, você saberá quando e como usá-las de forma eficiente no seu código.

## O Problema que Data Classes Resolvem

Em Java, para representar uma simples classe de dados como um `Usuario`, você precisaria escrever manualmente: construtor, getters, setters, `equals()`, `hashCode()` e `toString()`. São facilmente mais de 50 linhas de código para algo conceitualmente simples.

Em Kotlin, uma [data class](/glossario/data-class/) resolve tudo isso em uma única linha:

```kotlin
data class Usuario(val nome: String, val email: String, val idade: Int)
```

Essa única linha gera automaticamente todos os métodos mencionados acima. Vamos explorar cada um deles em detalhes.

## Sintaxe e Regras Básicas

Para declarar uma data class, basta usar a palavra-chave `data` antes de `class`. Existem algumas regras que o compilador exige:

```kotlin
// Data class valida
data class Produto(val nome: String, val preco: Double, val categoria: String)

// O construtor primário PRECISA ter pelo menos um parâmetro
// Todos os parâmetros do construtor primário devem ser val ou var
```

Regras obrigatórias para data classes:
- O construtor primário precisa ter **pelo menos um parâmetro**
- Cada parâmetro do construtor primário deve ser marcado como `val` ou `var`
- Data classes não podem ser `abstract`, `open`, `sealed` ou `inner`

## Métodos Gerados Automaticamente

### toString()

O `toString()` gerado é extremamente útil para debugging e logging:

```kotlin
data class Livro(val titulo: String, val autor: String, val paginas: Int)

val livro = Livro("O Senhor dos Anéis", "Tolkien", 1200)
println(livro)
// Livro(titulo=O Senhor dos Anéis, autor=Tolkien, paginas=1200)
```

Compare com uma classe regular, que imprimiria algo como `Livro@1a2b3c4d`. A data class mostra todos os valores de forma legível.

### equals() e hashCode()

Data classes comparam por **valor**, não por referência. Dois objetos com os mesmos dados são considerados iguais:

```kotlin
data class Ponto(val x: Int, val y: Int)

val p1 = Ponto(3, 7)
val p2 = Ponto(3, 7)
val p3 = Ponto(1, 5)

println(p1 == p2)  // true — mesmos valores
println(p1 == p3)  // false — valores diferentes
println(p1 === p2) // false — referências diferentes (são objetos distintos)

// hashCode tambem é consistente
val conjunto = hashSetOf(p1, p2, p3)
println(conjunto.size) // 2 — p1 e p2 são "iguais"
```

Isso é fundamental para usar data classes como chaves em `Map`, em `Set`, ou em qualquer situação que dependa de igualdade estrutural.

### copy()

O método `copy()` cria uma cópia do objeto, permitindo alterar apenas algumas propriedades. Isso é essencial para trabalhar com objetos imutáveis (declarados com `val`):

```kotlin
data class Configuração(
    val tema: String,
    val idioma: String,
    val notificacoes: Boolean,
    val fontSize: Int
)

val configOriginal = Configuração("escuro", "pt-BR", true, 16)

// Alterar apenas o tema e o tamanho da fonte
val configNova = configOriginal.copy(tema = "claro", fontSize = 18)

println(configOriginal)
// Configuração(tema=escuro, idioma=pt-BR, notificacoes=true, fontSize=16)

println(configNova)
// Configuração(tema=claro, idioma=pt-BR, notificacoes=true, fontSize=18)
```

O `copy()` é a base para o padrão de imutabilidade em Kotlin. Em vez de modificar um objeto existente, você cria uma nova versão com as alterações desejadas.

### componentN() e Destructuring

Data classes geram funções `component1()`, `component2()`, etc., correspondendo à ordem dos parâmetros no construtor. Isso habilita o [destructuring](/glossario/destructuring/):

```kotlin
data class Endereco(val rua: String, val numero: Int, val cidade: String)

val endereco = Endereco("Av. Paulista", 1000, "São Paulo")

// Destructuring declaration
val (rua, numero, cidade) = endereco
println("$rua, $numero — $cidade")
// Av. Paulista, 1000 — São Paulo

// Funciona em loops tambem
val enderecos = listOf(
    Endereco("Rua A", 10, "SP"),
    Endereco("Rua B", 20, "RJ"),
    Endereco("Rua C", 30, "BH")
)

for ((r, n, c) in enderecos) {
    println("$r, $n — $c")
}

// E com lambdas!
enderecos.forEach { (r, _, c) -> // _ ignora o numero
    println("$r em $c")
}
```

## Propriedades Fora do Construtor Primário

Propriedades declaradas **dentro do corpo** da data class não participam dos métodos gerados:

```kotlin
data class Aluno(val nome: String, val matricula: Int) {
    var notaFinal: Double = 0.0  // NÃO participa de equals, hashCode, toString, copy
}

val a1 = Aluno("Maria", 12345)
a1.notaFinal = 9.5

val a2 = Aluno("Maria", 12345)
a2.notaFinal = 7.0

println(a1 == a2) // true! — notaFinal NÃO é considerada
println(a1)       // Aluno(nome=Maria, matricula=12345) — sem notaFinal
```

Isso é intencional: apenas as propriedades do construtor primário definem a "identidade" do objeto. Use essa separação estrategicamente.

## Data Class vs Classe Regular

Quando usar data class e quando usar uma classe regular? Aqui está um comparativo claro:

```kotlin
// USE Data Class quando:
// - O objetivo principal é armazenar dados
// - Você precisa de equals/hashCode baseado em valores
// - Quer destructuring ou copy()

data class PedidoItem(val produtoId: Long, val quantidade: Int, val precoUnitario: Double)

// USE Classe Regular quando:
// - A classe tem comportamento complexo
// - Identidade é baseada em referência, nao em valores
// - Precisa de herança (data classes nao podem ser open)

class CarrinhoDeCompras {
    private val itens = mutableListOf<PedidoItem>()

    fun adicionar(item: PedidoItem) { itens.add(item) }
    fun total(): Double = itens.sumOf { it.precoUnitario * it.quantidade }
    fun limpar() { itens.clear() }
}
```

## Limitações de Data Classes

Existem limitações importantes que você deve conhecer:

```kotlin
// 1. Data classes NÃO podem ser open (nao permitem herança direta)
// data class Base(val x: Int) — nao pode ser herdada
// class Filha(x: Int, val y: Int) : Base(x) — ERRO!

// 2. Solução: use interfaces ou sealed classes
interface Forma {
    fun area(): Double
}

data class Circulo(val raio: Double) : Forma {
    override fun area() = Math.PI * raio * raio
}

data class Retangulo(val largura: Double, val altura: Double) : Forma {
    override fun area() = largura * altura
}

// 3. Data classes podem implementar interfaces normalmente
// 4. Data classes PODEM ser combinadas com sealed classes
```

## Exemplo Prático Completo

Vamos criar um sistema de gerenciamento de tarefas usando data classes:

```kotlin
enum class Prioridade { BAIXA, MEDIA, ALTA, URGENTE }
enum class Status { PENDENTE, EM_ANDAMENTO, CONCLUIDA, CANCELADA }

data class Tarefa(
    val id: Long,
    val titulo: String,
    val descricao: String,
    val prioridade: Prioridade,
    val status: Status = Status.PENDENTE,
    val tags: List<String> = emptyList()
)

fun main() {
    val tarefas = listOf(
        Tarefa(1, "Configurar CI/CD", "Pipeline do GitHub Actions", Prioridade.ALTA, tags = listOf("devops")),
        Tarefa(2, "Corrigir bug login", "Erro 401 no OAuth", Prioridade.URGENTE, Status.EM_ANDAMENTO, listOf("bug", "auth")),
        Tarefa(3, "Atualizar README", "Documentar nova API", Prioridade.BAIXA, tags = listOf("docs")),
        Tarefa(4, "Refatorar módulo X", "Aplicar SOLID", Prioridade.MEDIA, tags = listOf("refactor"))
    )

    // Filtrar e transformar usando destructuring
    val urgentes = tarefas
        .filter { it.prioridade == Prioridade.URGENTE || it.prioridade == Prioridade.ALTA }
        .sortedBy { it.prioridade }

    println("=== Tarefas Prioritárias ===")
    urgentes.forEach { (id, titulo, _, prioridade, status) ->
        println("#$id — $titulo [$prioridade] ($status)")
    }

    // Usar copy para atualizar status
    val tarefaAtualizada = tarefas[0].copy(status = Status.CONCLUIDA)
    println("\nAtualizada: $tarefaAtualizada")

    // Agrupar por status
    val porStatus = tarefas.groupBy { it.status }
    porStatus.forEach { (status, lista) ->
        println("\n$status: ${lista.map { it.titulo }}")
    }
}
```

## Erros Comuns

1. **Colocar propriedades mutáveis no construtor primário**: usar `var` no construtor de data classes pode gerar bugs, pois `equals()` e `hashCode()` dependem dos valores. Se o valor mudar após inserir o objeto em um `Set` ou como chave de um `Map`, você pode perder o acesso a ele. Prefira `val`.

2. **Esperar que propriedades do corpo participem de equals()**: como vimos, propriedades declaradas dentro do corpo da classe são ignoradas pelos métodos gerados. Isso é confuso para iniciantes e pode gerar comparações inesperadas.

3. **Tentar herdar de uma data class**: data classes não podem ser `open`. Se você precisa de hierarquias, use [sealed classes](/glossario/sealed-class/) combinadas com data classes ou use [interfaces](/glossario/interface/).

4. **Abusar de data classes para tudo**: nem toda classe deve ser uma data class. Classes com comportamento complexo, efeitos colaterais no construtor ou identidade baseada em referência devem ser classes regulares.

5. **Esquecer que copy() é shallow**: o `copy()` cria uma cópia rasa. Se a data class contém listas ou outros objetos mutáveis, a cópia compartilhará as mesmas referências internas.

## Conclusão e Próximos Passos

Data classes são um dos recursos mais práticos de Kotlin, eliminando centenas de linhas de boilerplate e tornando o código mais seguro e expressivo. Você aprendeu a sintaxe, os métodos gerados automaticamente, destructuring, o método `copy()`, as diferenças entre data classes e classes regulares, e as limitações que precisa conhecer.

Para continuar aprendendo, explore estes tópicos relacionados:

- [Sealed Classes](/tutoriais/sealed-classes-tutorial/) para criar hierarquias de tipos com data classes
- [Extension Functions](/tutoriais/extension-functions-tutorial/) para adicionar funcionalidades às suas data classes
- [Lambdas](/tutoriais/lambdas-kotlin/) para trabalhar com coleções de data classes de forma funcional
- [Generics](/tutoriais/generics-kotlin/) para criar data classes parametrizadas

Comece a usar data classes sempre que precisar representar dados. Elas são a ferramenta certa para DTOs, modelos de domínio, respostas de API e qualquer estrutura cujo propósito principal seja carregar informação.
