1. Code
  2. Coding Fundamentals

Kotlin Da Zero: Pacchetti e Funzioni Base

Scroll to top
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: Ranges and Collections
Kotlin From Scratch: More Fun With Functions

Italian (Italiano) translation by Mirko Pizii (you can also view the original English article)

Kotlin è un moderno linguaggio di programmazione che compone il bytecode di Java. È gratuito e open source e promette di rendere codifica per Android ancora più divertente.

Nell'articolo precedente, hai appreso sugli intervalli e delle collezioni in Kotlin. In questo tutorial, continueremo a imparare il linguaggio guardando come organizzare il codice utilizzando i pacchetti e poi continuare ad un'introduzione alle funzioni di Kotlin.

1. Pacchetti

Se hai familiarità con Java, sai che Java utilizza pacchetti per raggruppare classi correlate; ad esempio, il pacchetto java.util dispone di una serie di utili classi di utilità. I pacchetti sono dichiarati con la parola chiave package e qualsiasi file di Kotlin con una dichiarazione di package all'inizio può contenere dichiarazioni di classi, funzioni o interfacce.

Dichiarazione

Guardando il codice riportato di seguito, abbiamo dichiarato un pacchetto com.chikekotlin.projectx utilizzando la parola chiave package. Inoltre, abbiamo dichiarato una classe MyClass (discuteremo le classi in Kotlin nei post futuri) all'interno di questo pacchetto.

1
package com.chikekotlin.projectx
2
3
class MyClass

Ora, il nome completo per la classe MyClass è com.chikekotlin.projectx.MyClass.

1
package com.chikekotlin.projectx
2
3
fun saySomething(): String {
4
    return "How far?"
5
}

Nel codice di cui sopra abbiamo creato una funzione di livello superiore (ci arriveremo a breve). Così allo stesso modo di MyClass, il nome completo della funzione saySomething() è com.chikekotlin.projectx.saySomething.

Importazioni

In Kotlin, utilizziamo la dichiarazione di import per consentire al compilatore di localizzare le classi, le funzioni, le interfacce o gli oggetti da importare. In Java, invece, non è possibile importare direttamente le classi o le interfacce solo per metodi o funzioni.

Utilizziamo import per accedere a una funzione, un'interfaccia, una classe o un oggetto al di fuori del pacchetto in cui è stata dichiarata.

1
import com.chikekotlin.projectx.saySomething
2
3
fun main(args: Array<String>) {
4
    saySomething() // will print "How far?"

5
}

Nello snippet di codice di cui sopra abbiamo importato la funzione saySomething() da un pacchetto diverso e quindi abbiamo eseguito questa funzione.

Kotlin supporta anche le importazioni di wildcard utilizzando l'operatore *. Questo importa tutte le classi, le interfacce e le funzioni dichiarate nel pacchetto in una sola volta. Questo non è raccomandato, però, di solito è meglio rendere esplicite le vostre importazioni.

1
import com.chikekotlin.projectx.*

Import Aliasing

Quando si dispone di librerie che contengono nomi di classe o funzioni in conflitto (ad esempio, ciascuno dichiara una funzione con lo stesso nome), è possibile utilizzare la parola chiave as per assegnare un'entità importata a un nome temporaneo.

1
import com.chikekotlin.projectx.saySomething
2
import com.chikekotlin.projecty.saySomething as projectYSaySomething
3
4
fun main(args: Array<String>) {
5
    projectYSaySomething() 
6
}

Si noti che il nome temporaneo viene utilizzato solo nel file in cui è stato assegnato.

2. Funzioni

Una funzione raggruppa insieme una serie di istruzioni di codice che eseguono un'attività. I dettagli dell'implementazione della funzione sono nascosti dal chiamante.

In Kotlin, le funzioni vengono definite usando la parola chiave fun, come mostrato nell'esempio seguente:

1
fun hello(name: String): String {
2
    return "Hello $name"
3
}
4
val message = hello("Chike")
5
print(message) // will print "Hello Chike"

Nel codice qui sopra abbiamo definito una semplice funzione hello() con un solo parametro name di tipo String. Questa funzione restituisce un tipo String. Il formato di definizione dei parametri per le funzioni è name: type, ad esempio, age: Int, price: Double, student: StudentClass.

1
fun hello(name: String): Unit {
2
   print("Hello $name")
3
}
4
5
hello("Chike") // will print "Hello Chike"

La funzione sopra riportata è simile a quella precedente, ma nota che questo ha un tipo di ritorno Unit. Poiché questa funzione non restituisce alcun valore significativo a noi - stampa solo un messaggio - il suo tipo di ritorno è Unit per impostazione predefinita. Unit è un oggetto Kotlin (discuteremo gli oggetti Kotlin nei post successivi) che è simile ai tipi Void in Java e C.

1
public object Unit {
2
    override fun toString() = "kotlin.Unit"
3
}

Si noti che se non dichiari esplicitamente il tipo di ritorno come Unit, il tipo viene dedotto dal compilatore.

1
fun hello(name: String) { // will still compile

2
   print("Hello $name")
3
}

Funzioni a linea singola

Le funzioni di una riga o di una riga sono funzioni che sono solo espressioni singole. In questa funzione, ci liberiamo dalle parentesi graffe e usiamo il simbolo = prima dell'espressione. In altre parole, ci liberiamo del blocco funzionale.

1
fun calCircumference(radius: Double): Double {
2
    return (2 * Math.PI) * radius
3
}

La funzione sopra riportata può essere abbreviata in una singola riga:

1
fun calCircumference(radius: Double) = (2 * Math.PI) * radius

Guardando la funzione aggiornata sopra, puoi vedere che abbiamo reso più preciso il codice rimuovendo le parentesi graffe riccamente {}, la parola chiave return e anche il tipo di restituzione (dedotto dal compilatore).

È comunque possibile includere il tipo di ritorno per essere più esplicito se si desidera.

1
fun calCircumference(radius: Double): Double = (2 * Math.PI) * radius

Parametri denominati

I parametri denominati consentono funzionalità leggibili nominando i parametri che vengono passati ad una funzione quando viene chiamata.

Nel seguente esempio, abbiamo creato una funzione che stampa il mio nome completo.

1
fun sayMyFullName(firstName: String, lastName: String, middleName: String): Unit {
2
    print("My full name is $firstName $middleName $lastName");
3
}

Per eseguire la funzione di sopra, faremo soltanto una chiamata come:

1
sayMyFullName("Chike", "Nnamdi", "Mgbemena")

Guardando la chiamata di funzione sopra, non sappiamo quali argomenti tipo String corrispondano a quali parametri di funzione (anche se alcuni IDE come IntelliJ IDEA possono aiutarci). Gli utenti della funzione dovranno esaminare la firma di funzione (o il codice sorgente) o la documentazione per sapere quale corrisponde ad ogni parametro.

1
sayMyFullName(firstName = "Chike", middleName = "Nnamdi", lastName = "Mgbemena")

Nella seconda chiamata di funzione sopra abbiamo fornito i nomi dei parametri prima dei valori di argomento. È possibile vedere che questa chiamata di funzione è più chiara e leggibile di quella precedente. Questo modo di chiamare funzioni contribuisce a ridurre la possibilità di errori che possono accadere quando gli argomenti dello stesso tipo vengono scambiati per errore.

Il chiamante può anche modificare l'ordine dei parametri usando i parametri nominati. Per esempio:

1
sayMyFullName(lastName = "Mgbemena", middleName = "Nnamdi", firstName = "Chike") // will still compile

Nel codice precedente abbiamo scambiato la posizione di argomento del firstName con lastName. L'ordine di argomento non importa con i parametri nominati perché il compilatore mapperà ciascuno di essi al parametro di funzione corretto.

Parametri predefiniti

In Kotlin, possiamo dare un valore di default di funzione per uno qualsiasi dei suoi parametri. Questi valori predefiniti vengono utilizzati se nulla è assegnato agli argomenti durante la chiamata di funzione. Per farlo in Java, dovremmo creare diversi metodi di overloading.

Qui, nel nostro metodo calCircumference(), abbiamo modificato il metodo aggiungendo un valore predefinito per il parametro pi - Math.PI, una costante dal pacchetto java.lang.Math.

1
fun calCircumference(radius: Double, pi: Double = Math.PI): Double = (2 * pi) * radius

Quando chiamiamo questa funzione, possiamo passare il nostro valore approssimato per pi o utilizzare l'impostazione predefinita.

1
print(calCircumference(24.0)) // used default value for PI and prints 150.79644737231007

2
print(calCircumference(24.0, 3.14)) // passed value for PI and prints 150.72

Vediamo un altro esempio.

1
fun printName(firstName: String, middleName: String = "N/A", lastName: String) {
2
    println("first name: $firstName - middle name: $middleName - last name: $lastName")
3
}

Nel seguente codice abbiamo cercato di chiamare la funzione, ma non si compila:

1
printName("Chike", "Mgbemena") // won't compile

Nella chiamata di funzione sopra, passa il mio nome e cognome alla funzione e spero di utilizzare il valore predefinito per il nome intermedio. Ma questo non verrà compilato perché il compilatore è confuso. Non sa cosa sia l'argomento "Mgbemena", è per il middleName o il parametro lastName?

Per risolvere questo problema, possiamo combinare parametri nominali e parametri predefiniti.

1
printName("Chike", lastName = "Mgbemena") // will now compile

Java interoperabilità

Dato che Java non supporta i valori dei parametri predefiniti nei metodi, è necessario specificare esplicitamente tutti i valori dei parametri quando si chiama una funzione di Kotlin da Java. Ma Kotlin ci fornisce le funzionalità per rendere più facile per i chiamanti Java annotando la funzione di Kotlin con @JvmOverloads. Questa annotazione indicherà il compilatore Kotlin per generare le funzioni sovraccaricate di Java per noi.

Nell'esempio seguente abbiamo annotato la funzione calCirumference() con @JvmOverloads.

1
@JvmOverloads
2
fun calCircumference(radius: Double, pi: Double = Math.PI): Double = (2 * pi) * radius

Il codice riportato di seguito è stato generato dal compilatore Kotlin in modo che i chiamanti Java possano scegliere quale chiamata.

1
// Java

2
double calCircumference(double radius, double pi);
3
double calCircumference(double radius);

Nell'ultima definizione del metodo Java generato, il parametro pi è stato omesso. Ciò significa che il metodo utilizza il valore pi predefinito.

Argomenti illimitati

In Java, possiamo creare un metodo per ricevere un numero non specificato di argomenti includendo un ellipse (...) dopo un tipo nell'elenco dei parametri del metodo. Questo concetto è supportato anche dalle funzioni di Kotlin con l'utilizzo del modificatore vararg seguito dal nome del parametro.

1
fun printInts(vararg ints: Int): Unit {
2
    for (n in ints) {
3
        print("$n\t")
4
    }
5
}
6
printInts(1, 2, 3, 4, 5, 6) // will print 1    2  3	4	5	6

Il modificatore vararg consente ai chiamanti di passare in un elenco di argomenti separati da virgole. Dietro le scene, questa lista di argomenti verranno avvolti in un array.

Quando una funzione ha più parametri, il parametro vararg è tipicamente l'ultimo. È anche possibile avere parametri dopo il vararg, ma dovrai utilizzare parametri nominali per specificarli quando chiami la funzione.

1
fun printNumbers(myDouble: Double, myFloat: Float, vararg ints: Int) {
2
    println(myDouble)
3
    println(myFloat)
4
    for (n in ints) {
5
        print("$n\t")
6
    }
7
}
8
printNumbers(1.34, 4.4F, 2, 3, 4, 5, 6) // will compile

Ad esempio, nel codice precedente, il parametro con il modificatore vararg è nell'ultima posizione in un elenco di parametri multipli (questo è quello che di solito facciamo). Ma cosa succede se non lo vogliamo nell'ultima posizione? Nell'esempio seguente, è nella seconda posizione.

1
fun printNumbers(myDouble: Double, vararg ints: Int, myFloat: Float) {
2
    println(myDouble)
3
    println(myFloat)
4
    for (n in ints) {
5
        print("$n\t")
6
    }
7
}
8
printNumbers(1.34, 2, 3, 4, 5, 6, myFloat = 4.4F) // will compile

9
printNumbers(1.34, ints = 2, 3, 4, 5, 6, myFloat = 4.4F) // will not compile

10
printNumbers(myDouble = 1.34, ints = 2, 3, 4, 5, 6, myFloat = 4.4F) // will also not

11
compile

Come si può osservare nel codice aggiornato di cui sopra, abbiamo usato argomenti denominati sull'ultimo parametro per risolvere questo problema.

Operatore Spread

Diciamo che vogliamo passare una serie di interi alla nostra funzione printNumbers(). La funzione prevede che i valori siano sbloccati in un elenco di parametri, però. Se si tenta di passare l'array direttamente a printNumbers(), vedrai che non verrà compilato.

1
val intsArray: IntArray = intArrayOf(1, 3, 4, 5)
2
printNumbers(1.34, intsArray, myFloat = 4.4F) // won't compile

Per risolvere questo problema, dobbiamo usare l'operatore di diffusione *. Questo operatore disfare l'array e quindi passerà i singoli elementi come argomenti nella funzione per noi.

1
val intsArray: IntArray = intArrayOf(1, 3, 4, 5)
2
printNumbers(1.34, *intsArray, myFloat = 4.4F) // will now compile

Inserendo l'operatore di diffusione * davanti a intsArray nell'elenco degli argomenti della funzione, il codice ora compila e produce lo stesso risultato come se avessimo passato gli elementi di intsArray come un elenco separato da virgole di argomenti.

Ritornare più valori

A volte desideriamo restituire più valori da una funzione. Un modo è utilizzare il tipo Pair in Kotlin per creare un Pair e quindi restituirlo. La struttura di questo Pair racchiude due valori che possono essere successivamente accessibili. Questo tipo Kotlin può accettare qualsiasi tipo di fornitura del suo costruttore. Inoltre, i due tipi non devono nemmeno essere gli stessi.

1
fun getUserNameAndState(id: Int): Pair<String?, String?> {
2
    require(id > 0, { "Error: id is less than 0" })
3
4
    val userNames: Map<Int, String> = mapOf(101 to "Chike", 102 to "Segun", 104 to "Jane")
5
    val userStates: Map<Int, String> = mapOf(101 to "Lagos", 102 to "Imo", 104 to "Enugu")
6
7
    val userName = userNames[id]
8
    val userState = userStates[id]
9
    return Pair(userName, userState)
10
}

Nella funzione sopra abbiamo costruito un nuovo Pair passando le variabili userName e userState come primo e secondo argomento rispettivamente al suo costruttore e quindi restituito questo Pair al chiamante.

Un'altra cosa da notare è che abbiamo usato una funzione chiamata require() nella funzione getUserNameAndState(). Questa funzione di aiuto dalla biblioteca standard viene utilizzata per dare ai nostri chiamanti di funzione una condizione preliminare per incontrare, altrimenti verrà lanciata un'IlllegalArgumentException (discuteremo le eccezioni in Kotlin in un post futuro). Il secondo argomento opzionale per require() è un letterale funzione che restituisce un messaggio da visualizzare se viene generata l'eccezione. Ad esempio, chiamare la funzione getUserNameAndState() e passare -1 come un argomento ad esso innescherà:

IntelliJ IDEA code execution resultIntelliJ IDEA code execution resultIntelliJ IDEA code execution result

Recuperare dati da un Pair

1
val userNameAndStatePair: Pair<String?, String?> = getUserNameAndState(101)
2
println(userNameAndStatePair.first) // Chike

3
println(userNameAndStatePair.second) // Lagos

Nel codice precedente abbiamo acceduto ai primi e ai secondi valori dal tipo Pair utilizzando la sua prima e seconda proprietà.

Tuttavia, c'è un modo migliore di farlo: la destrutturazione.

1
val (name, state) = getUserNameAndState(101)
2
println(name) // Chike

3
println(state) // Lagos

Quello che abbiamo fatto nel codice aggiornato di cui sopra è quello di assegnare direttamente il primo e il secondo valore del tipo Pair restituito rispettivamente al nome e allo stato delle variabili. Questa funzione è chiamata dichiarazione di deformazione.

Triplo valori di ritorno e oltre

Ora, cosa succede se si desidera restituire tre valori contemporaneamente? Kotlin ci fornisce un altro tipo utile chiamato Triple.

1
fun getUserNameStateAndAge(id: Int): Triple<String?, String?, Int> {
2
    require(id > 0, { "id is less than 0" })
3
    
4
    val userNames: Map<Int, String> = mapOf(101 to "Chike", 102 to "Segun", 104 to "Jane")
5
    val userStates: Map<Int, String> = mapOf(101 to "Lagos", 102 to "Imo", 104 to "Enugu")
6
    
7
    val userName = userNames[id]
8
    val userState = userStates[id]
9
    val userAge = 6
10
    return Triple(userNames[id], userStates[id], userAge)
11
}
12
13
val (name, state, age) = getUserNameStateAndAge(101)
14
println(name) // Chike

15
println(state) // Lagos

16
println(age) // 6

Sono sicuro che alcuni di voi stanno chiedendo cosa fare se si desidera restituire più di tre valori. La risposta per questo sarà in un post successivo, quando discutiamo le classi di dati di Kotlin.

Conclusione

In questo tutorial, hai imparato i pacchetti e le funzioni di base nel linguaggio di programmazione di Kotlin. Nel tutorial successivo della serie Kotlin Da Zero, potrai saperne di più sulle funzioni di Kotlin. A presto!

Per saperne di più sulla lingua Kotlin, consiglio di visitare la documentazione di Kotlin. Oppure vedi alcuni dei nostri altri post di sviluppo di applicazioni Android qui su Envato Tuts+!