Kotlin Language

Le funzioni - basi

Kotlin prevede una copertura completa ed efficiente delle funzioni che, naturalmente, ricoprono un ruolo importante nel linguaggio.
La denizione formale è molto semplice è fa uso, come già avrete intuito, della keyword fun

fun nomefunzione (eventuali parametri) evnetuale tipo di ritorno
{
corpo della funzione
}

L'esempio che segue è realmente di base:

  Esempio 7.1
1
2
3
4
5
6
7
8
9
fun saluta()
{
  println("ciao")
}

fun main(args: Array<String>)
{
  saluta()
}

come si vede la funzione è dichiarata dalla riga 1 alla 4 ed è richiamata alla 8. In questo caso non ci sono nè parametri nè valori di ritorno. Un esempio più completo è il seguente:

  Esempio 7.2
1
2
3
4
5
6
7
8
9
10
11
fun doppio(x: Int) :Int
{
val x1 = x + x
return x1
}

fun main(args: Array<String>)
{
val y = doppio(2)
println(y)
}

La funzione doppio vuole un parametro di tipo intero e restituisce un intero a sua volta. In questo caso è necessario usare la keyword return per esporre il risultato che si vuole restituire. I parametri possono essere da uno a quanti si vuole mentre, anche per i valori di ritorno, altrove che è possibile retituirne più di uno. Per ora possiamo aggiungere che il  valore di ritorno può essere direttamente assegnato ad una variabile, che deve essere dello stesso tipo del valore di ritorno stesso ovviamente, come avviene alla riga 9, dove la funzione viene richiamata contestualmente all'operazione di assegnazione. Tutto molto standard.
I parametri, se più di uno, devono essere separati tramite la virgola e ciascuno deve essere tipizzato. Ad esempio

fun funz(x: Int, y: Int, s: String)

In Kotlin è possibile attribuire dei valori di default ai parametri (default parameters) ovvero valori che possono essere usati qualora non vengano passati dal chiamante. Il quale chiamante invece può imporre dei valori diversi. Il seguente esempio dovrebbe chiarire la cosa:

  Esempio 7.3
1
2
3
4
5
6
7
8
9

10
11
12
13
14
fun trenumeri(x:Int = 1, y:Int = 2, z: Int = 3) : Int
{
  var somma: Int = 0
  somma = x + y + z
  return somma
}

fun main(args: Array<String>): Unit
{
  println(trenumeri(4,5,6))
  println(trenumeri(4,5))
  println(trenumeri(4))
  println(trenumeri())
}

che ha come output:

15
12
9
6

come è facile intuire, il primo risultato è dovuto ai parametri che vengono paassati mentre l'ultimo è integralmente frutto dei valori di default. L'attribuzione è ovviamente poiszionale partendo da sinistra ovvero, ad esempio, il valore 4 passato nella chiamata alla riga 12 sovrappone il valore 1 attribuito di deafult alla variabile x definita alla riga 1. Un caso che si può presentare è quello di fornire dei valori di default solo ad alcuni parametri. Se ad esempio la definizione della funzione trenumeri alla riga 1 fosse:

fun trenumeri(x:Int = 1, y: Int, z: Int = 3) : Int

le righe 10 e 11 vanno bene ma la 12 no perchè mancherebbe un valore per il parametro y e ovviamente anche la 13 conterrebbe una chiamata sbagliata. In questo senso, capiamo che l'attribuzione è strettamente posizionale, ovvero non tiene contro della presenza o no dei valori di default. Questo problema può essere superato attraverso un'altra feature del linguaggio ovvero i named parameters, i parametri con nome. Usando questa possibilità possiamo richiamare i parametri attraverso il loro identificatore.

  Esempio 7.4
1
2
3
4
5
6
7
8
9
10
11
12
13
fun trenumeri(x:Int = 3, y:Int, z: Int = 9) : Int
{
  var somma: Int = 0
  somma = x + y + z
  return somma
}

fun main(args: Array<String>): Unit
{
  println(trenumeri(4,5,6))
  println(trenumeri(4,5))
  println(trenumeri(y = 4))
}

Questo programma funziona ed ha come output:

15
18
16

che potete facilmente giustificare da soli. La riga di nostro interesse è naturalmente la 12 che invia un parametro alla funzione attribuendolo esattamente a y, l'unico che non ne aveva uno di default.

Proseguendo secondo il nostro cammino, è interessante notare come Kotlin accetti le funzioni innestate (sul sito le chiamano local functions) ovvero funzioni definite in altre funzioni e alle quali possiamo passare dei parametri ricevendone normalmente dei valori di ritorno

  Esempio 7.5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun saluto()
{
  fun Stringa(s1: String) : String
  {
    return s1 + s1
  }
  println(Stringa("ciao"))
}

fun main(args: Array<String>): Unit
{
  println("Ora ti saluto")
  saluto()
}

Interessante è anche annotare la possibilità di rappresentare le funzioni in modo molto sintetico come nel seguente esempio

Esempio 7.6
1
2
3
4
5
6
7
fun doppio(x: Int): Int = x * 2

fun main(args: Array<String>): Unit
{
  val x = doppio(5)
  println(x)
}


Come vedete alla riga 1 la funzione è stata definita in maniera comptta e senza bisogno di usare "return". Tra l'altro il tipo  di ritorno può essere eliminato se deducibile via inferenza.

Avrete notato che la funzione main restituisce, in alcini degli esempi precedenti, un valore di tipo Unit. Si tratta di quello che ritorna da funzioni che non devono restituire nulla. Unit è un tipo che ha un solo valore, ovvero Unit stesso. E' possibile non esplicitarlo.

Sulle funzioni avremo altro da dire ma per ora può bastare quanto espresso.