O que é reified em Kotlin?
A palavra-chave reified permite acessar informações de tipo genérico em tempo de execução. Normalmente, por causa do type erasure da JVM, tipos genéricos são apagados após a compilação. Com reified, o Kotlin mantém essa informação disponível.
Para usar reified, a função precisa ser inline. As duas coisas andam juntas.
O problema sem reified
// Isso NÃO funciona:
fun <T> ehDoTipo(valor: Any): Boolean {
// return valor is T // Erro! Não dá pra checar tipo genérico
return false
}
Na JVM, o tipo T é apagado em tempo de execução. O compilador não sabe qual tipo é T quando o código roda.
A solução com reified
inline fun <reified T> ehDoTipo(valor: Any): Boolean {
return valor is T
}
fun main() {
println(ehDoTipo<String>("Kotlin")) // true
println(ehDoTipo<Int>("Kotlin")) // false
println(ehDoTipo<Int>(42)) // true
}
Com reified, o tipo T é conhecido em tempo de execução. Você pode usar is T, T::class e até criar instâncias (com reflexão).
Exemplo prático: parsing de JSON
inline fun <reified T> parsear(json: String): T {
println("Parseando para ${T::class.simpleName}")
// Aqui voce usaria uma biblioteca como Gson ou Moshi
// return Gson().fromJson(json, T::class.java)
throw NotImplementedError("Exemplo simplificado")
}
Sem reified, seria necessário passar Class<T> como parâmetro extra. Com reified, a informação de tipo já tá disponível.
Filtrando por tipo em coleções
inline fun <reified T> List<Any>.filtrarPorTipo(): List<T> {
return this.filterIsInstance<T>()
}
fun main() {
val mistura = listOf(1, "Kotlin", 2.5, "Brasil", 3, true)
val strings = mistura.filtrarPorTipo<String>()
println(strings) // [Kotlin, Brasil]
val inteiros = mistura.filtrarPorTipo<Int>()
println(inteiros) // [1, 3]
}
Criando instâncias com reified
Com reified você pode acessar o construtor de um tipo genérico via reflexão, algo impossível sem essa palavra-chave:
inline fun <reified T : Any> criarInstancia(): T {
val construtor = T::class.constructors.firstOrNull { it.parameters.isEmpty() }
?: throw IllegalArgumentException("${T::class.simpleName} nao tem construtor sem argumentos")
return construtor.call()
}
class Motor {
val tipo = "V8"
override fun toString() = "Motor(tipo=$tipo)"
}
class Roda {
val aro = 17
override fun toString() = "Roda(aro=$aro)"
}
fun main() {
val motor = criarInstancia<Motor>()
val roda = criarInstancia<Roda>()
println(motor) // Motor(tipo=V8)
println(roda) // Roda(aro=17)
}
Reified com funções de extensão
A combinação de reified com funções de extensão cria APIs muito elegantes. Veja um exemplo inspirado em frameworks de injeção de dependência:
class Container {
private val servicos = mutableMapOf<String, Any>()
fun registrar(servico: Any) {
servicos[servico::class.qualifiedName ?: ""] = servico
}
inline fun <reified T : Any> obter(): T {
val chave = T::class.qualifiedName
return servicos[chave] as? T
?: throw IllegalStateException("Serviço ${T::class.simpleName} nao registrado")
}
}
class BancoDeDados {
fun consultar(sql: String) = "Resultado de: $sql"
}
class ServicoDeEmail {
fun enviar(para: String) = println("Email enviado para $para")
}
fun main() {
val container = Container()
container.registrar(BancoDeDados())
container.registrar(ServicoDeEmail())
val db = container.obter<BancoDeDados>()
println(db.consultar("SELECT * FROM usuarios"))
val email = container.obter<ServicoDeEmail>()
email.enviar("karina@kotlin.dev.br")
}
Limitações
- Só funciona com funções
inline - Não pode ser usado em classes
- Não pode ser usado com parâmetros
noinline - Não pode ser chamado a partir de código Java (funções inline com reified não são visíveis para Java)
- Funções inline com reified não podem ser armazenadas em variáveis de tipo função
Casos de Uso no Mundo Real
- Serialização e desserialização: bibliotecas como Gson, Moshi e kotlinx.serialization usam reified para determinar o tipo de destino ao converter JSON, XML ou outros formatos em objetos Kotlin.
- Injeção de dependência: frameworks como Koin usam
inline fun <reified T> get(): Tpara resolver dependências sem passarClass<T>explicitamente, tornando a API muito mais limpa. - Logging com tipo automático: criar funções de log que automaticamente incluem o nome da classe sem precisar receber parâmetros adicionais, como
inline fun <reified T> T.logger() = LoggerFactory.getLogger(T::class.java). - Navegação no Android: usar reified para criar funções de navegação tipadas, como
inline fun <reified T : Activity> Context.iniciarActivity(), eliminando a necessidade de passar a classe explicitamente.
Boas Práticas
- Use
reifiedapenas quando realmente precisar acessar o tipo em runtime. Se a lógica não depende deT::class,is Tou reflexão, não precisa de reified. - Lembre-se de que funções
inlinecomreifiedsão copiadas em cada ponto de chamada. Mantenha o corpo da função enxuto para não inflar o bytecode. - Combine com upper bounds (
<reified T : AlgumaInterface>) para restringir os tipos aceitos e garantir segurança em tempo de compilação. - Prefira
reifieda receberClass<T>ouKClass<T>como parâmetro. A API fica mais limpa e idiomática. - Documente as restrições de tipo quando usar reified em APIs públicas, já que o compilador nem sempre consegue dar mensagens de erro claras sobre tipos incompatíveis.
Erros Comuns
- Esquecer de marcar a função como
inline: o compilador vai reclamar, mas a mensagem de erro pode confundir iniciantes. Lembre-se:reifiedexigeinline, sempre. - Criar funções inline muito grandes com reified: como o corpo da função é copiado em cada chamada, funções longas geram muito bytecode duplicado. Extraia a lógica não-inline para funções auxiliares.
- Tentar usar reified em parâmetros de classe:
class Repositorio<reified T>não funciona. Reified é exclusivo de funções inline. Para classes, passeKClass<T>no construtor. - Assumir que reified funciona com herança de tipos:
ehDoTipo<Number>(42)retornafalseparais Numberem alguns cenários com tipos primitivos, porque a JVM trata primitivos de forma especial. - Chamar funções reified a partir de Java: código Java não consegue chamar funções inline com reified. Se sua API precisa ser compatível com Java, forneça uma alternativa que receba
Class<T>.
Perguntas Frequentes
Por que reified precisa de inline? Porque o mecanismo funciona substituindo o corpo da função no ponto de chamada. Ao fazer isso, o compilador conhece o tipo concreto usado e pode inserir a informação de tipo diretamente no bytecode gerado.
Posso usar reified com múltiplos parâmetros de tipo? Sim. Você pode ter vários parâmetros reified na mesma função, como inline fun <reified A, reified B> converter(valor: A): B. Cada um será resolvido independentemente.
Qual a diferença entre reified e passar Class<T> como parâmetro? Funcionalmente, o resultado é o mesmo. A diferença é ergonomia: com reified, você escreve parsear<Usuario>(json) em vez de parsear(json, Usuario::class.java). O código fica mais limpo e idiomático.
O reified tem impacto na performance? O impacto é o mesmo do inline em geral: o corpo da função é copiado em cada chamada, o que pode aumentar o tamanho do bytecode mas elimina o overhead de chamada de função. Para funções pequenas, o impacto é negligível ou até positivo.
Termos Relacionados
- Inline Function — funções que são expandidas no ponto de chamada, requisito para reified
- Generics — o sistema de tipos genéricos do Kotlin
- Extension Function — funções de extensão que combinam bem com reified
- Reflection — reflexão em Kotlin, frequentemente usada junto com reified
reified é uma ferramenta que resolve elegantemente um problema chato da JVM. Sempre que precisar acessar o tipo genérico em runtime, essa é a saída.