Neste tutorial, vamos explorar em profundidade o sistema de funções do Kotlin. Funções são blocos de código reutilizáveis que realizam uma tarefa específica e são a base de qualquer programa. Você vai aprender a declarar funções com a palavra-chave fun, definir parâmetros e tipos de retorno, usar default parameters, named arguments, single-expression functions, vararg e funções locais. Ao final, você terá domínio completo sobre como criar e organizar funções em Kotlin.

Declarando Funções com fun

Em Kotlin, toda função é declarada usando a palavra-chave fun. A estrutura básica inclui o nome da função, a lista de parâmetros entre parênteses e, opcionalmente, o tipo de retorno após dois-pontos. Se a função não retorna nenhum valor útil, o tipo de retorno é Unit, que pode ser omitido.

fun saudacao() {
    println("Olá, bem-vindo ao Kotlin Brasil!")
}

fun somar(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    saudacao()
    val resultado = somar(10, 20)
    println("Soma: $resultado") // Soma: 30
}

Diferente de linguagens como Java, em Kotlin as funções não precisam estar dentro de uma classe. Você pode declarar funções no nível superior do arquivo, o que chamamos de funções top-level. Isso reduz a necessidade de criar classes utilitárias com métodos estáticos e torna o código mais direto e limpo.

Parâmetros e Tipos de Retorno

Todo parâmetro em Kotlin precisa ter o tipo declarado explicitamente. Isso garante segurança de tipos em tempo de compilação. O tipo de retorno também deve ser declarado, exceto quando o compilador consegue inferi-lo automaticamente em single-expression functions.

fun calcularDesconto(preco: Double, percentual: Double): Double {
    val desconto = preco * (percentual / 100)
    return preco - desconto
}

fun ehMaiorDeIdade(idade: Int): Boolean {
    return idade >= 18
}

fun imprimirDetalhes(nome: String, idade: Int): Unit {
    println("Nome: $nome, Idade: $idade")
}

fun main() {
    println(calcularDesconto(100.0, 15.0)) // 85.0
    println(ehMaiorDeIdade(21)) // true
    imprimirDetalhes("Carlos", 30) // Nome: Carlos, Idade: 30
}

Observe que o tipo Unit é equivalente ao void do Java, mas em Kotlin ele é um tipo real. Quando o retorno é Unit, você pode omitir tanto a declaração do tipo quanto a instrução return.

Default Parameters (Parâmetros Padrão)

Uma das funcionalidades mais práticas do Kotlin é a possibilidade de definir valores padrão para parâmetros. Isso elimina a necessidade de múltiplas sobrecargas de função, algo muito comum em Java.

fun criarUsuario(
    nome: String,
    email: String,
    ativo: Boolean = true,
    papel: String = "usuario"
): String {
    return "Usuário: $nome | Email: $email | Ativo: $ativo | Papel: $papel"
}

fun main() {
    // Usando todos os parâmetros
    println(criarUsuario("Ana", "ana@email.com", false, "admin"))

    // Usando apenas os obrigatórios (ativo=true, papel="usuario")
    println(criarUsuario("João", "joao@email.com"))

    // Misturando: passando ativo, mas usando papel padrão
    println(criarUsuario("Maria", "maria@email.com", true))
}

Com default parameters, você pode ter uma única função que atende a vários cenários de uso. Os parâmetros com valor padrão devem, em geral, ser colocados no final da lista de parâmetros, facilitando a chamada sem named arguments.

Named Arguments (Argumentos Nomeados)

Named arguments permitem que você passe argumentos fora de ordem, referenciando-os pelo nome. Isso é especialmente útil em funções com muitos parâmetros ou quando vários parâmetros têm o mesmo tipo.

fun configurarServidor(
    host: String = "localhost",
    porta: Int = 8080,
    ssl: Boolean = false,
    timeout: Int = 30
) {
    println("Servidor: $host:$porta | SSL: $ssl | Timeout: ${timeout}s")
}

fun main() {
    // Chamada tradicional
    configurarServidor("meusite.com", 443, true, 60)

    // Com named arguments - muito mais legível
    configurarServidor(
        host = "meusite.com",
        porta = 443,
        ssl = true,
        timeout = 60
    )

    // Pulando parâmetros com valor padrão
    configurarServidor(ssl = true, timeout = 120)
    // Resultado: Servidor: localhost:8080 | SSL: true | Timeout: 120s
}

A combinação de default parameters com named arguments é extremamente poderosa. Ela permite criar APIs flexíveis e legíveis sem a complexidade de padrões como Builder.

Single-Expression Functions (Funções de Expressão Única)

Quando uma função possui apenas uma expressão, você pode simplificar a sintaxe removendo as chaves e o return, usando o operador = diretamente. O compilador infere o tipo de retorno automaticamente.

// Forma tradicional
fun dobrar(valor: Int): Int {
    return valor * 2
}

// Single-expression function
fun dobrarSimplificado(valor: Int) = valor * 2

fun ehPar(numero: Int) = numero % 2 == 0

fun formatarNome(primeiro: String, ultimo: String) = "$primeiro $ultimo".trim()

fun maximo(a: Int, b: Int) = if (a > b) a else b

fun main() {
    println(dobrarSimplificado(21)) // 42
    println(ehPar(10)) // true
    println(formatarNome("Kotlin", "Brasil")) // Kotlin Brasil
    println(maximo(15, 23)) // 23
}

Single-expression functions são idiomáticas em Kotlin e usadas extensivamente no dia a dia. Elas tornam o código conciso sem sacrificar a legibilidade, especialmente para funções pequenas e auxiliares.

Vararg: Número Variável de Argumentos

O modificador vararg permite que uma função receba um número variável de argumentos do mesmo tipo. Internamente, o Kotlin trata esses argumentos como um array.

fun calcularMedia(vararg notas: Double): Double {
    if (notas.isEmpty()) return 0.0
    return notas.sum() / notas.size
}

fun imprimirTodos(prefixo: String, vararg mensagens: String) {
    for (msg in mensagens) {
        println("$prefixo: $msg")
    }
}

fun main() {
    println(calcularMedia(7.5, 8.0, 9.2, 6.8)) // 7.875
    println(calcularMedia(10.0, 9.5)) // 9.75

    imprimirTodos("LOG", "Iniciando", "Processando", "Concluído")

    // Passando um array com o operador spread (*)
    val valores = doubleArrayOf(8.0, 7.0, 9.0)
    println(calcularMedia(*valores)) // 8.0
}

Quando você já tem um array e quer passá-lo para uma função vararg, use o operador spread (*). Note que só é possível ter um parâmetro vararg por função, e ele normalmente é o último parâmetro, embora possa estar em outra posição se os demais argumentos forem passados como named arguments.

Funções Locais

Kotlin permite declarar funções dentro de outras funções. Essas funções locais têm acesso às variáveis do escopo externo (closure) e são úteis para encapsular lógica auxiliar que não faz sentido expor fora da função principal.

fun processarPedido(itens: List<String>, desconto: Double) {
    // Função local com acesso ao parâmetro 'desconto'
    fun calcularPreco(precoBase: Double): Double {
        return precoBase * (1 - desconto / 100)
    }

    fun validarItem(item: String): Boolean {
        return item.isNotBlank() && item.length > 2
    }

    for (item in itens) {
        if (validarItem(item)) {
            val preco = calcularPreco(50.0)
            println("Item: $item - Preço com desconto: R$${"%.2f".format(preco)}")
        }
    }
}

fun main() {
    val meusItens = listOf("Camiseta", "Calça", "", "Tênis")
    processarPedido(meusItens, 10.0)
}

Funções locais são ideais quando você precisa reutilizar um trecho de código dentro de uma função, mas essa lógica não é relevante em nenhum outro contexto do programa. Elas ajudam a manter funções grandes organizadas sem poluir o escopo do arquivo.

Erros Comuns

Esquecer o tipo de retorno em funções complexas. Em funções com múltiplas linhas, o Kotlin não infere o tipo de retorno. Você precisa declará-lo explicitamente. Single-expression functions são a exceção.

// ERRO: funções com corpo em bloco precisam declarar o tipo de retorno
// fun somar(a: Int, b: Int) { return a + b }

// CORRETO
fun somar(a: Int, b: Int): Int { return a + b }

Confundir a posição do vararg. Se o vararg não for o último parâmetro, os demais argumentos devem ser passados com named arguments, o que frequentemente confunde iniciantes.

Não aproveitar default parameters. Muitos desenvolvedores vindos de Java criam várias sobrecargas de funções, quando um único método com valores padrão resolveria o problema de forma mais elegante.

Usar !! dentro de funções sem tratamento. Ao receber tipos nullable, evite forçar a conversão com !!. Prefira safe calls ou verificações explícitas.

Funções muito longas. Se a função tem mais de 20-30 linhas, considere extrair partes dela em funções locais ou em funções separadas. Funções pequenas e focadas são mais fáceis de testar e manter.

Conclusão e Próximos Passos

Neste tutorial, você aprendeu os fundamentos das funções em Kotlin: desde a declaração básica com fun até recursos avançados como default parameters, named arguments, single-expression functions, vararg e funções locais. Essas ferramentas tornam o Kotlin uma linguagem expressiva e produtiva para construir aplicações de qualquer porte.

O próximo passo natural é explorar higher-order functions e lambdas, que levam o conceito de funções a um nível ainda mais poderoso. Também recomendamos estudar extension functions, que permitem adicionar funcionalidades a classes existentes sem herança. Com domínio completo de funções, você terá uma base sólida para avançar nos tutoriais sobre classes e objetos e programação orientada a objetos em Kotlin.