Preparar-se para entrevistas tecnicas com Kotlin exige conhecer nao apenas a sintaxe, mas tambem os conceitos por tras das decisoes de design da linguagem. Neste guia, compilamos as perguntas mais frequentes em processos seletivos, com respostas detalhadas e exemplos praticos.
Perguntas sobre Fundamentos
1. Qual a diferenca entre val e var?
Esta e quase sempre a primeira pergunta. A resposta vai alem do basico:
val nome = "Kotlin" // Referencia imutavel (similar a final em Java)
var contador = 0 // Referencia mutavel
// Importante: val nao significa que o OBJETO e imutavel
val lista = mutableListOf(1, 2, 3)
lista.add(4) // Funciona! A referencia nao muda, mas o conteudo sim
// Para imutabilidade real, use colecoes imutaveis
val listaImutavel = listOf(1, 2, 3)
// listaImutavel.add(4) // Nao compila!
O ponto chave e explicar que val torna a referencia imutavel, nao o objeto em si. Para imutabilidade profunda, voce precisa usar tipos imutaveis.
2. Explique Null Safety em Kotlin
// Tipos nullable vs non-nullable
var nome: String = "Kotlin" // Nao pode ser null
var apelido: String? = null // Pode ser null
// Operadores de null safety
val tamanho1: Int? = apelido?.length // Safe call
val tamanho2: Int = apelido?.length ?: 0 // Elvis operator
val tamanho3: Int = apelido!!.length // Not-null assertion (perigoso)
// Smart cast
fun processar(valor: String?) {
if (valor != null) {
// Aqui, valor e automaticamente String (nao String?)
println(valor.length)
}
}
// let para trabalhar com nullables
apelido?.let { nomeNaoNulo ->
println("Apelido: $nomeNaoNulo")
println("Tamanho: ${nomeNaoNulo.length}")
}
Mencione que null safety elimina NullPointerException em tempo de compilacao, e que o operador !! deve ser evitado em codigo de producao.
3. O que sao Data Classes?
data class Usuario(
val id: String,
val nome: String,
val email: String,
val ativo: Boolean = true
)
// O compilador gera automaticamente:
// - equals() e hashCode() baseados nas propriedades do construtor primario
// - toString() formatado
// - copy() para criar copias com modificacoes
// - componentN() para destructuring
val user = Usuario("1", "Ana", "ana@email.com")
val copia = user.copy(nome = "Maria")
// Destructuring
val (id, nome, email) = user
println("$nome ($email)")
// Util em colecoes
val usuarios = listOf(user, copia)
val nomes = usuarios.map { (_, nome, _) -> nome }
4. Sealed Classes vs Enum Classes
// Enum - Conjunto fixo de constantes
enum class Cor { VERMELHO, VERDE, AZUL }
// Sealed class - Hierarquia restrita com dados diferentes por tipo
sealed class Resultado {
data class Sucesso(val dados: String) : Resultado()
data class Erro(val mensagem: String, val codigo: Int) : Resultado()
data object Carregando : Resultado()
}
fun tratar(resultado: Resultado): String {
return when (resultado) {
is Resultado.Sucesso -> "OK: ${resultado.dados}"
is Resultado.Erro -> "Erro ${resultado.codigo}: ${resultado.mensagem}"
is Resultado.Carregando -> "Aguarde..."
}
// O when e EXAUSTIVO - o compilador verifica todos os casos
}
A diferenca chave: enums tem instancias fixas e identicas em estrutura, sealed classes permitem subtipos diferentes com dados diferentes.
Perguntas sobre Coroutines
5. O que sao Coroutines e como funcionam?
// Coroutines sao threads leves gerenciadas pelo Kotlin
// Uma suspend function so pode ser chamada de outra suspend function
// ou de um coroutine builder
suspend fun buscarDados(): List<String> {
delay(1000) // Suspende sem bloquear a thread
return listOf("dado1", "dado2")
}
// Coroutine builders
fun exemplo(scope: CoroutineScope) {
// launch - Fire and forget, retorna Job
scope.launch {
val dados = buscarDados()
processar(dados)
}
// async - Retorna Deferred com resultado
scope.launch {
val deferred = async { buscarDados() }
val dados = deferred.await()
}
// runBlocking - Bloqueia a thread atual (usar apenas em testes/main)
runBlocking {
val dados = buscarDados()
}
}
6. Explique Dispatchers e Structured Concurrency
// Dispatchers controlam em qual thread a coroutine executa
suspend fun exemploDispatchers() {
// Dispatchers.Main - Thread principal (UI no Android)
// Dispatchers.IO - Otimizado para I/O (rede, disco)
// Dispatchers.Default - Otimizado para CPU
withContext(Dispatchers.IO) {
// Operacoes de rede ou banco de dados aqui
}
withContext(Dispatchers.Default) {
// Calculos pesados aqui
}
}
// Structured concurrency garante que coroutines filhas
// sao canceladas quando o escopo pai e cancelado
suspend fun buscarTudo() = coroutineScope {
val usuarios = async { api.getUsuarios() }
val produtos = async { api.getProdutos() }
// Se api.getUsuarios() falhar, api.getProdutos() e cancelado automaticamente
Pair(usuarios.await(), produtos.await())
}
7. Qual a diferenca entre Flow, StateFlow e SharedFlow?
// Flow - Stream frio (cold), emite valores sob demanda
fun temperaturas(): Flow<Double> = flow {
while (true) {
emit(sensor.lerTemperatura())
delay(1000)
}
}
// StateFlow - Sempre tem um valor atual, hot stream
class ContadorViewModel : ViewModel() {
private val _contagem = MutableStateFlow(0)
val contagem: StateFlow<Int> = _contagem.asStateFlow()
fun incrementar() {
_contagem.update { it + 1 }
}
}
// SharedFlow - Hot stream sem valor inicial, configuracel
class EventBus {
private val _eventos = MutableSharedFlow<Evento>(
replay = 0,
extraBufferCapacity = 64,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val eventos: SharedFlow<Evento> = _eventos.asSharedFlow()
suspend fun emitir(evento: Evento) {
_eventos.emit(evento)
}
}
Perguntas sobre Funcoes
8. Extension Functions e Scope Functions
// Extension functions adicionam funcionalidades a tipos existentes
fun String.isValidEmail(): Boolean {
return matches(Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"))
}
val valido = "user@email.com".isValidEmail() // true
// Scope functions: let, run, with, apply, also
data class Configuracao(
var host: String = "",
var porta: Int = 0,
var timeout: Long = 0
)
// apply - Configura um objeto e retorna ele mesmo
val config = Configuracao().apply {
host = "localhost"
porta = 8080
timeout = 5000
}
// let - Transforma valor, util com nullables
val resultado = texto?.let { it.trim().uppercase() }
// also - Executa acao lateral, retorna o objeto original
val lista = mutableListOf(1, 2, 3).also {
println("Lista criada com ${it.size} elementos")
}
// run - Executa bloco no contexto do objeto, retorna resultado
val tamanhoFormatado = texto.run {
trim()
"Texto tem $length caracteres"
}
9. Higher-Order Functions e Lambdas
// Higher-order function recebe ou retorna funcoes
fun <T> List<T>.customFilter(
predicate: (T) -> Boolean
): List<T> {
val resultado = mutableListOf<T>()
for (item in this) {
if (predicate(item)) {
resultado.add(item)
}
}
return resultado
}
// Uso
val pares = listOf(1, 2, 3, 4, 5).customFilter { it % 2 == 0 }
// inline - Evita overhead de criacao de objetos lambda
inline fun <T> medirTempo(bloco: () -> T): Pair<T, Long> {
val inicio = System.currentTimeMillis()
val resultado = bloco()
val duracao = System.currentTimeMillis() - inicio
return Pair(resultado, duracao)
}
Perguntas de Design e Arquitetura
10. Como voce estrutura um projeto Kotlin?
// Clean Architecture tipica
// domain/ - Regras de negocio puras
// model/
// repository/ (interfaces)
// usecase/
// data/ - Implementacoes de acesso a dados
// repository/ (implementacoes)
// remote/
// local/
// presentation/ - UI e ViewModels
// screen/
// viewmodel/
// component/
// Exemplo de Use Case
class TransferirDinheiroUseCase(
private val contaRepository: ContaRepository,
private val transacaoRepository: TransacaoRepository
) {
suspend operator fun invoke(
origem: ContaId,
destino: ContaId,
valor: Money
): Result<Transacao> = runCatching {
require(valor.isPositive()) { "Valor deve ser positivo" }
val contaOrigem = contaRepository.buscar(origem)
?: throw ContaNaoEncontradaException(origem)
require(contaOrigem.saldo >= valor) { "Saldo insuficiente" }
val transacao = Transacao(
origem = origem,
destino = destino,
valor = valor,
timestamp = Instant.now()
)
contaRepository.debitar(origem, valor)
contaRepository.creditar(destino, valor)
transacaoRepository.salvar(transacao)
}
}
Dicas para a Entrevista
- Explique seu raciocinio: Nao apenas de a resposta, explique por que
- Mencione trade-offs: Mostra maturidade tecnica
- Use exemplos reais: Relacione conceitos com situacoes praticas
- Admita quando nao sabe: E muito melhor que inventar uma resposta
- Faca perguntas: Mostra interesse genuino na empresa e no projeto
Conclusao
Preparar-se para entrevistas Kotlin vai alem de decorar respostas. O objetivo e demonstrar que voce entende os conceitos profundamente e sabe aplica-los em cenarios reais. Pratique escrevendo codigo, revise os conceitos fundamentais e mantenha-se atualizado com as novidades da linguagem. Com preparacao adequada, voce estara confiante para enfrentar qualquer processo seletivo.