Pular para o conteúdo

Trilha de Kotlin · parte 4 de 59

Funções: parâmetros, retorno e argumentos nomeados

Como declarar funções em Kotlin com fun, parâmetros, tipo de retorno, Unit, valores padrão e argumentos nomeados, com saídas reais de cada trecho.

iniciantePublicada em · 12 min de leitura

Tamanho da fonte:
Largura da coluna:
Tom:
Espaço entre linhas:
Fonte:

Funções: parâmetros, retorno e argumentos nomeados

Pense numa máquina de café de escritório. Você põe pó e água, aperta um botão e recebe o café, sem saber o que acontece lá dentro. Uma em Kotlin é isso: um trecho com nome que recebe alguns ingredientes, faz uma tarefa e devolve um resultado. Quem chama precisa lembrar o nome e os ingredientes, e nada mais.

Esta aula cobre a declaração com fun, os parâmetros e o retorno, a função que não devolve valor (Unit), os valores padrão, os argumentos nomeados e o das variáveis. Lambdas, funções de ordem superior e extensões ficam para outras aulas. Os trechos rodaram com Kotlin 2.4.21, pelo Gradle 9.7.0 e o JDK 25, e as mensagens de erro foram copiadas do compilador.

o mapa da aula

A seção 1 mostra por que a função existe. As seções 2 a 6 cobrem a declaração, Unit, a forma com =, valores padrão, argumentos nomeados e escopo. A seção 7 é um exercício guiado, a 8 traz os erros comuns, a 9 os exercícios com resposta e a 10 o glossário.

1. para que serve uma função

Imagine calcular o preço com desconto de três produtos, copiando a conta três vezes:

fun main() {
    val preco1 = 20
    val desconto1 = preco1 / 10
    println("café: ${preco1 - desconto1}")

    val preco2 = 30
    val desconto2 = preco2 / 10
    println("bolo: ${preco2 - desconto2}")

    val preco3 = 10
    val desconto3 = preco3 / 10
    println("suco: ${preco3 - desconto3}")
}
café: 18
bolo: 27
suco: 9

Nenhuma conta está errada. O problema chega na semana em que o desconto passa de 10% para 15%: você procura três linhas e torce para não ter esquecido uma. Uma função dá nome à regra e a deixa num lugar só, para que ela seja testada e trocada sem caça ao tesouro.

2. declarar uma função com fun

Uma declaração começa com fun, seguida do nome em camelCase. Os parâmetros ficam entre parênteses no formato nome: Tipo, e o vem depois dos parênteses, com dois-pontos. Quando o corpo tem chaves, o tipo de retorno precisa estar escrito, porque o Kotlin não o deduz nesse caso. A única exceção é Unit, tratada na seção 3, e a forma de uma linha só aparece na seção 4.

fun precoComDesconto(preco: Int): Int {
    return preco - preco / 10
}

fun main() {
    val precoFinal = precoComDesconto(20)
    println("café: $precoFinal")
    println("bolo: ${precoComDesconto(30)}")
    println("suco: ${precoComDesconto(10)}")
}
café: 18
bolo: 27
suco: 9

Em precoComDesconto(20), o 20 é o argumento, copiado para o preco. O parâmetro é só de leitura, e a seção 8 mostra o que acontece quando você tenta mudá-lo.

A figura acompanha essa chamada. O azul é o código que chama, o verde são valores, e o terracota é o trabalho da própria função. O 20 entra como cópia, a conta acontece na caixa do meio, e o 18 volta pela seta laranja até ser guardado. O rótulo lembra que preco só existe dentro da função. Os pacotes mostram a ordem dos passos, não o tempo que cada um leva.

Quem chama manda 20 ao parâmetro preco, o return calcula 18 dentro da função e o valor volta até a val precoFinal.
figura 1 · Chamada de função: o vira cópia no parâmetro e o return devolve o resu

3. : quando não há valor para devolver

Quando não há resultado útil, como numa função que imprime uma mensagem ou grava um arquivo, o tipo de retorno é Unit. Se o bloco não declara retorno, o compilador assume Unit sozinho, e escrever : Unit é opcional. Quem vem de Java conhece void, mas void não é um tipo. A documentação diz que Unit é um tipo de verdade, e por isso o resultado pode ser guardado numa variável, como no exemplo.

fun anunciarPedido(produto: String) {
    println("pedido pronto: $produto")
}

fun aceitarPreco(preco: Int) {
    if (preco <= 0) return
    println("preço aceito: $preco")
}

fun main() {
    anunciarPedido("café")
    aceitarPreco(-5)
    aceitarPreco(20)
    val resultado = anunciarPedido("bolo")
    println("valor devolvido: $resultado")
}
pedido pronto: café
preço aceito: 20
pedido pronto: bolo
valor devolvido: kotlin.Unit

A função aceitarPreco usa return sem valor para sair antes da impressão quando o preço não é positivo. Isso só faz sentido em funções Unit.

Existe um vizinho, Nothing, de funções que nunca chegam a devolver nada. TODO() é o exemplo: ao ser chamada, lança NotImplementedError. Esse comportamento foi observado na execução, porque a página de funções da documentação não trata de Nothing.

4. função de

Quando o corpo é uma única expressão, troque as chaves e o return por =. A convenção de estilo do Kotlin pede essa forma para corpos de uma linha. Com =, o compilador deduz o retorno: em dobro, o Int vem da multiplicação. A dedução não vale para funções recursivas, nem para funções com return no corpo, que precisam do tipo escrito. Em código de biblioteca, a convenção pede o tipo explícito nas funções públicas, para que uma mudança interna não altere o contrato.

fun precoComDesconto(preco: Int): Int = preco - preco / 10

fun dobro(x: Int) = x * 2

fun main() {
    println("café: ${precoComDesconto(20)}")
    println("dobro de 7: ${dobro(7)}")
}
café: 18
dobro de 7: 14

5. valores padrão e argumentos nomeados

Um parâmetro pode ter com =, e quem chama pode omiti-lo. Na função pedido, o café padrão é coado, sem açúcar e sem leite.

fun pedido(tipo: String = "coado", acucar: Int = 0, leite: Boolean = false): String {
    val complemento = if (leite) "com leite" else "sem leite"
    return "café $tipo, açúcar $acucar, $complemento"
}

fun main() {
    println(pedido())
    println(pedido("espresso"))
    println(pedido(acucar = 2))
    println(pedido("espresso", leite = true))
    println(pedido(leite = true, tipo = "espresso"))
    println(pedido("espresso", 1, true))
}
café coado, açúcar 0, sem leite
café espresso, açúcar 0, sem leite
café coado, açúcar 2, sem leite
café espresso, açúcar 0, com leite
café espresso, açúcar 0, com leite
café espresso, açúcar 1, com leite

Um escreve o nome do parâmetro antes do valor. Com nome, a ordem não importa: pedido(leite = true, tipo = "espresso") funciona. Você também pode pular um parâmetro com padrão e nomear só o que interessa, como em pedido("espresso", leite = true). Depois do primeiro argumento pulado, os seguintes precisam de nome, regra que a documentação lista e o compilador confere.

Isso substitui boa parte da sobrecarga. Em Java, cada combinação de opções pede um método próprio. Com três parâmetros opcionais já são oito combinações possíveis, e uma única função com padrões cobre todas elas.

A figura liga cada argumento ao parâmetro de mesmo nome. Os argumentos estão na ordem da declaração para o desenho ficar limpo, mas a chamada escrita começa pelo leite, e a ligação continua sendo pelo nome. Laranja é o que quem chama escolheu, verde é o padrão que entra quando nada foi passado, e a seta pontilhada mostra que o açúcar não veio da chamada.

Argumentos nomeados ligam cada valor ao parâmetro de mesmo nome, mesmo fora de ordem, e o açúcar cai no valor padrão 0.
figura 2 · Argumentos nomeados: cada valor vai ao parâmetro de mesmo nome, e acucar cai no

Um parâmetro marcado com vararg aceita zero ou mais valores do mesmo tipo e, dentro da função, vira um array.

6. funções de topo e escopo de variáveis

Nenhuma função desta aula está dentro de uma classe. Uma função pode morar direto no arquivo, no nível de topo, como o main da aula de ambiente. Em Java, o mesmo código exigiria uma classe, mesmo sem estado algum.

Uma variável declarada dentro da função existe enquanto ela roda, e cada chamada cria a sua. No exemplo, contar imprime 1 nas duas chamadas, porque o n recomeça em zero.

fun contar() {
    var n = 0
    n++
    println("n dentro de contar: $n")
}

fun dobrar() {
    var n = 100
    n *= 2
    println("n dentro de dobrar: $n")
}

fun main() {
    contar()
    contar()
    dobrar()
}
n dentro de contar: 1
n dentro de contar: 1
n dentro de dobrar: 200

Uma função declarada dentro de outra é uma . Ela enxerga e pode alterar as variáveis da função de fora. No exemplo, acumular soma cada preço no total de somarPrecos:

fun somarPrecos(): Int {
    var total = 0
    fun acumular(preco: Int) {
        total += preco
    }
    acumular(10)
    acumular(20)
    acumular(30)
    return total
}

Para os preços 10, 20 e 30, a função devolve 60.

Pergunta de entrevistaComo valores padrão e argumentos nomeados em Kotlin se comparam à sobrecarga de métodos em Java?

Um parâmetro pode ter valor padrão, e quem chama pode omiti-lo. Com argumento nomeado, escrevo o nome do parâmetro antes do valor e a ordem deixa de importar, então posso pular um parâmetro com padrão e informar só o que interessa. Na aula, a função pedido tem café coado, sem açúcar e sem leite como padrão, e a chamada pedido(leite = true, tipo = “espresso”) funciona. Em Java, cada combinação de opções pede um método próprio: com três parâmetros opcionais são oito combinações. Uma única função com padrões cobre todas. O cuidado é que, depois do primeiro argumento pulado, os seguintes precisam de nome, e a chamada com nomes fica mais legível quando há vários parâmetros do mesmo tipo.

7. exercício guiado: a máquina de café

Monte a função pedido em três passos.

No passo 1, escreva fun pedido(tipo: String): String = "café $tipo" e chame com pedido("espresso"). A saída deve ser café espresso.

No passo 2, acrescente acucar: Int = 0 e inclua o açúcar no texto. Chame com pedido("espresso") e com pedido("espresso", acucar = 2).

No passo 3, acrescente leite: Boolean = false, escolha o complemento com um if usado como expressão, como na aula de Controle de fluxo, e chame com leite = true e com os nomes trocados de ordem.

Quando as chamadas derem o que a seção 5 mostra, o exercício está feito. O texto de cada linha pode ser diferente, desde que os padrões e os nomes estejam certos.

8. erros comuns e boas práticas

esquecer o tipo de retorno em função de bloco

A função dobro tem chaves e não declara o retorno. Ela calcula x * 2, mas não entrega o resultado, porque falta o return.

fun dobro(x: Int) {
    x * 2
}

fun main() {
    val y: Int = dobro(4)
    println(y)
}

O compilador responde:

e: file:///.../src/main/kotlin/Main.kt:6:16 Initializer type mismatch: expected 'Int', actual 'Unit'.

Em tradução livre, o valor inicial não bate: era esperado Int e veio Unit. Com return x * 2 e sem : Int, a mensagem seria Return type mismatch: expected 'Unit', actual 'Int'.. Nos dois casos, a correção é declarar o tipo: fun dobro(x: Int): Int.

Boa prática: use = quando o corpo é uma expressão, e declare o retorno sempre que usar chaves.

parâmetro não é variável

Parâmetro é val, como a documentação diz, e a função abaixo não compila.

fun incrementar(n: Int): Int {
    n = n + 1
    return n
}

fun main() {
    println(incrementar(1))
}
e: file:///.../src/main/kotlin/Main.kt:2:5 'val' cannot be reassigned.

Em tradução livre, 'val' não pode ser reatribuída. Quando o valor precisa mudar, copie o parâmetro para uma var:

fun aplicarDesconto(preco: Int): Int {
    var atual = preco
    atual = atual - atual / 10
    return atual
}

fun main() {
    val precoOriginal = 20
    println("com desconto: ${aplicarDesconto(precoOriginal)}")
    println("preço original: $precoOriginal")
}
com desconto: 18
preço original: 20

O original não muda, porque a função mexeu só na cópia. Com Int, a cópia é do número. Com um objeto, a cópia é da referência, e uma função pode alterar o objeto que quem chamou também enxerga. Esse cuidado volta na aula de coleções.

usar uma variável fora do escopo

fun calcularTotal(preco: Int, quantidade: Int): Int {
    val subtotal = preco * quantidade
    return subtotal
}

fun main() {
    println(calcularTotal(10, 3))
    println(subtotal)
}
e: file:///.../src/main/kotlin/Main.kt:8:13 Unresolved reference 'subtotal'.

Em tradução livre, a referência subtotal não foi encontrada. A variável existe só dentro de calcularTotal. Se main precisa do valor, a função deve devolvê-lo.

misturar nomeados e posicionais

Dá para misturar os dois tipos de argumento, mas não em qualquer ordem:

fun pedido(tipo: String = "coado", acucar: Int = 0, leite: Boolean = false): String =
    "café $tipo, açúcar $acucar, leite $leite"

fun main() {
    println(pedido(acucar = 2, "espresso"))
}
e: file:///.../src/main/kotlin/Main.kt:5:32 Mixing named and positional arguments is not allowed unless the order of the arguments matches the order of the parameters.

A mensagem diz que a mistura só vale quando a ordem dos argumentos coincide com a dos parâmetros. Aqui o acucar vem antes do tipo. Um nome digitado errado gera No parameter with name 'acucarr' found., que quer dizer que não existe parâmetro com esse nome.

Boa prática: nomeie o argumento quando o valor sozinho não diz o que é, como true ou 2.

9. exercícios com resposta

  1. Escreva areaRetangulo(largura: Int, altura: Int): Int como expressão única e chame com 3 e 4. Antes de rodar, diga o resultado.
  2. Acrescente percentual: Int = 10 a precoComDesconto, faça a função usá-lo, e chame com 50 e 20%, e depois com 50 e o padrão.
  3. Sem rodar, diga se pedido(acucar = 2, "espresso") compila.

Respostas dos exercícios 1 e 2:

fun areaRetangulo(largura: Int, altura: Int): Int = largura * altura

fun precoComDesconto(preco: Int, percentual: Int = 10): Int = preco - preco * percentual / 100

fun main() {
    println("área: ${areaRetangulo(3, 4)}")
    println("50 com 10%: ${precoComDesconto(50)}")
    println("50 com 20%: ${precoComDesconto(50, 20)}")
    println("50 com 20%, por nome: ${precoComDesconto(preco = 50, percentual = 20)}")
}
área: 12
50 com 10%: 45
50 com 20%: 40
50 com 20%, por nome: 40

Resposta do exercício 3: não compila, pelo mesmo motivo da seção 8.

10. glossário

  • Função: trecho de código com nome que recebe dados, executa uma tarefa e pode devolver um valor.
  • Parâmetro: nome declarado na função, com tipo, que recebe o valor da chamada. É somente leitura.
  • Argumento: valor passado na chamada para um parâmetro.
  • Tipo de retorno: tipo do valor devolvido, escrito depois dos parênteses.
  • Unit: tipo das funções que não devolvem valor útil. Num bloco sem retorno declarado, o compilador assume Unit.
  • Nothing: tipo de funções que nunca chegam a devolver valor, como TODO().
  • Expressão única: função cujo corpo é uma expressão só, escrita com =.
  • Valor padrão: valor que um parâmetro recebe quando o argumento é omitido.
  • Argumento nomeado: argumento escrito com o nome do parâmetro, como leite = true, que pode vir em qualquer ordem.
  • vararg: parâmetro que aceita zero ou mais valores do mesmo tipo.
  • Função de topo: função declarada direto no arquivo, sem classe.
  • Função local: função declarada dentro de outra função.
  • Escopo: trecho do código onde um nome é visível.

Fontes

Consultadas em 04/11/2026.

Os trechos rodaram com Kotlin 2.4.21 pelo plugin Gradle, com Gradle 9.7.0 e JDK 25. O comportamento de TODO() e o fato de vararg virar array foram observados na execução. Não verifiquei os cliques do IntelliJ.

O próximo passo é Null safety e tipos nuláveis, onde a pergunta sobre a ausência de valor volta, agora com tipos que aceitam null.

Relacionados

↑ Voltar ao topo

Atalhos de teclado

Abrir busca
/
Alternar tema claro/escuro
t
Aula anterior na série
←
Próxima aula na série
→
Lista de atalhos
?