Classi e istanze di oggetti in Kotlin

Per i codelab di questo percorso, creerai un'app Android per il lancio dei dadi. Quando l'utente "tira i dadi", verrà generato un risultato casuale. Il risultato tiene conto del numero di facce del dado. Ad esempio, da un dado a 6 facce possono uscire solo valori compresi tra 1 e 6.

Ecco l'aspetto dell'app finale.

Per aiutarti a concentrarti sui nuovi concetti di programmazione per questa app, utilizzerai lo strumento di programmazione Kotlin basato su browser per creare le funzionalità principali dell'app. Il programma restituirà i risultati alla console. In seguito implementerai l'interfaccia utente in Android Studio.

In questo primo codelab, creerai un programma Kotlin che simula il lancio di dadi e restituisce un numero casuale, proprio come farebbe un dado.

Prerequisiti

  • Come aprire, modificare ed eseguire il codice in https://try.kotlinlang.org/
  • Crea ed esegui un programma Kotlin che utilizza variabili e funzioni e stampa un risultato nella console.
  • Formatta i numeri all'interno del testo utilizzando un modello di stringa con la notazione ${variable}.

Obiettivi didattici

  • Come generare numeri casuali in modo programmatico per simulare il lancio di dadi.
  • Come strutturare il codice creando una classe Dice con una variabile e un metodo.
  • Come creare un'istanza di oggetto di una classe, modificarne le variabili e chiamarne i metodi.

Cosa creerai

  • Un programma Kotlin nello strumento di programmazione Kotlin basato su browser che può eseguire un tiro di dado casuale.

Cosa serve

  • Un computer con una connessione a internet

I giochi spesso hanno un elemento casuale. Potresti vincere un premio casuale o avanzare di un numero casuale di passi sul tabellone. Nella vita di tutti i giorni, puoi utilizzare numeri e lettere casuali per generare password più sicure.

Invece di lanciare dadi reali, puoi scrivere un programma che simuli il lancio dei dadi per te. Ogni volta che tiri i dadi, il risultato può essere un numero qualsiasi all'interno dell'intervallo di valori possibili. Fortunatamente, non devi creare il tuo generatore di numeri casuali per un programma di questo tipo. La maggior parte dei linguaggi di programmazione, incluso Kotlin, offre un modo integrato per generare numeri casuali. In questa attività utilizzerai il codice Kotlin per generare un numero casuale.

Configurare il codice di avvio

  1. Nel browser, apri il sito web https://try.kotlinlang.org/.
  2. Elimina tutto il codice esistente nell'editor di codice e sostituiscilo con il codice riportato di seguito. Questa è la funzione main() con cui hai lavorato nei codelab precedenti (vedi il codelab Scrivi il tuo primo programma Kotlin).
fun main() {

}

Utilizzare la funzione casuale

Per tirare un dado, devi avere un modo per rappresentare tutti i valori validi del tiro. Per un dado normale a 6 facce, i tiri accettabili sono: 1, 2, 3, 4, 5 e 6.

In precedenza hai appreso che esistono tipi di dati come Int per i numeri interi e String per il testo. IntRange è un altro tipo di dati e rappresenta un intervallo di numeri interi da un punto iniziale a un punto finale. IntRange è un tipo di dati adatto per rappresentare i possibili valori che può produrre il lancio di un dado.

  1. All'interno della funzione main(), definisci una variabile come val chiamata diceRange. Assegnalo a un IntRange da 1 a 6, che rappresenta l'intervallo di numeri interi che può essere estratto da un dado a 6 facce.
val diceRange = 1..6

Puoi capire che 1..6 è un intervallo Kotlin perché ha un numero iniziale, due punti e un numero finale (senza spazi tra i numeri). Altri esempi di intervalli di numeri interi sono 2..5 per i numeri da 2 a 5 e 100..200 per i numeri da 100 a 200.

Analogamente a come la chiamata println() indica al sistema di stampare il testo specificato, puoi utilizzare una funzione chiamata random() per generare e restituire un numero casuale per un determinato intervallo. Come in precedenza, puoi archiviare il risultato in una variabile.

  1. All'interno di main(), definisci una variabile come val denominata randomNumber.
  2. Imposta randomNumber in modo che abbia il valore del risultato della chiamata di random() nell'intervallo diceRange, come mostrato di seguito.
 val randomNumber = diceRange.random()

Nota che stai chiamando random() su diceRange utilizzando un punto tra la variabile e la chiamata di funzione. Puoi leggere questa espressione come "generazione di un numero casuale da diceRange". Il risultato viene quindi memorizzato nella variabile randomNumber.

  1. Per visualizzare il numero generato in modo casuale, utilizza la notazione di formattazione delle stringhe (chiamata anche "modello di stringa") ${randomNumber} per stamparlo, come mostrato di seguito.
println("Random number: ${randomNumber}")

Il codice finale dovrebbe avere questo aspetto.

fun main() {
    val diceRange = 1..6
    val randomNumber = diceRange.random()
    println("Random number: ${randomNumber}")
}
  1. Esegui il codice più volte. Ogni volta, dovresti vedere un output come quello riportato di seguito, con numeri casuali diversi.
Random number: 4

Quando tiri i dadi, sono oggetti reali nelle tue mani. Anche se il codice che hai appena scritto funziona perfettamente, è difficile immaginare che si tratti di dadi reali. Organizzare un programma in modo che assomigli di più alle cose che rappresenta lo rende più facile da comprendere. Sarebbe bello avere dei dadi programmatici da lanciare.

Tutti i dadi funzionano essenzialmente allo stesso modo. Hanno le stesse proprietà, ad esempio i lati, e lo stesso comportamento, ad esempio possono essere arrotolati. In Kotlin, puoi creare un progetto programmatico di un dado che indica che i dadi hanno facce e possono tirare un numero casuale. Questo progetto è chiamato classe.

Da questa classe, puoi quindi creare oggetti dado effettivi, chiamati istanze oggetto. Ad esempio, puoi creare un dado a 12 facce o a 4 facce.

Definisci una classe Dice

Nei passaggi successivi, definirai una nuova classe chiamata Dice per rappresentare un dado rotolante.

  1. Per ricominciare da capo, cancella il codice nella funzione main() in modo da ottenere il codice mostrato di seguito.
fun main() {

}
  1. Sotto questa funzione main(), aggiungi una riga vuota, quindi aggiungi il codice per creare la classe Dice. Come mostrato di seguito, inizia con la parola chiave class, seguita dal nome della classe, seguito da una parentesi graffa aperta e chiusa. Lascia spazio tra le parentesi graffe per inserire il codice del corso.
class Dice {

}

All'interno di una definizione di corso, puoi specificare una o più proprietà per il corso utilizzando le variabili. I dadi reali possono avere un numero di facce, un colore o un peso. In questa attività, ti concentrerai sulla proprietà del numero di lati del dado.

  1. All'interno della classe Dice, aggiungi una variabile var chiamata sides per il numero di facce del dado. Imposta sides su 6.
class Dice {
    var sides = 6
}

È tutto. Ora hai una classe molto semplice che rappresenta i dadi.

Crea un'istanza della classe Dice

Con questa classe Dice, hai un progetto di come è un dado. Per avere un dado effettivo nel tuo programma, devi creare un'istanza dell'oggetto Dice. Se ti servissero tre dadi, creeresti tre istanze dell'oggetto.

  1. Per creare un'istanza dell'oggetto Dice, nella funzione main(), crea un val denominato myFirstDice e inizializzalo come istanza della classe Dice. Nota le parentesi dopo il nome della classe, che indicano che stai creando una nuova istanza dell'oggetto dalla classe.
fun main() {
    val myFirstDice = Dice()
}

Ora che hai un oggetto myFirstDice, un elemento creato dal progetto, puoi accedere alle sue proprietà. L'unica proprietà di Dice è sides. Accedi a una proprietà utilizzando la "notazione punto". Pertanto, per accedere alla proprietà sides di myFirstDice, chiami myFirstDice.sides, che si pronuncia "myFirstDice punto sides".

  1. Sotto la dichiarazione di myFirstDice, aggiungi un'istruzione println() per restituire il numero di sides di myFirstDice.
println(myFirstDice.sides)

Il codice dovrebbe essere simile al seguente.

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
}

class Dice {
    var sides = 6
}
  1. Esegui il programma e dovrebbe restituire il numero di sides definito nella classe Dice.
6

Ora hai una classe Dice e un dado vero e proprio myFirstDice con 6 sides.

Facciamo rotolare i dadi!

Tira il dado

In precedenza hai utilizzato una funzione per eseguire l'azione di stampare gli strati della torta. Anche il lancio dei dadi è un'azione che può essere implementata come funzione. Poiché tutti i dadi possono essere tirati, puoi aggiungere una funzione per farlo all'interno della classe Dice. Una funzione definita all'interno di una classe è chiamata anche metodo.

  1. Nella classe Dice, sotto la variabile sides, inserisci una riga vuota e poi crea una nuova funzione per tirare i dadi. Inizia con la parola chiave Kotlin fun, seguita dal nome del metodo, dalle parentesi () e dalle parentesi graffe di apertura e chiusura {}. Puoi lasciare una riga vuota tra le parentesi graffe per fare spazio ad altro codice, come mostrato di seguito. Il tuo corso dovrebbe avere questo aspetto.
class Dice {
    var sides = 6

    fun roll() {

    }
}

Quando tiri un dado a sei facce, viene generato un numero casuale compreso tra 1 e 6.

  1. All'interno del metodo roll(), crea un val randomNumber. Assegnagli un numero casuale nell'intervallo 1..6. Utilizza la notazione con il punto per chiamare random() nell'intervallo.
val randomNumber = (1..6).random()
  1. Dopo aver generato il numero casuale, stampalo nella console. Il metodo roll() completato dovrebbe avere l'aspetto del codice riportato di seguito.
fun roll() {
     val randomNumber = (1..6).random()
     println(randomNumber)
}
  1. Per eseguire il roll myFirstDice, in main() chiama il metodo roll() su myFirstDice. Chiami un metodo utilizzando la "notazione punto". Pertanto, per chiamare il metodo roll() di myFirstDice, digita myFirstDice.roll(), che si pronuncia "myFirstDice punto roll()".
myFirstDice.roll()

Il codice completato dovrebbe avere questo aspetto.

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
    myFirstDice.roll()
}

class Dice {
    var sides = 6

    fun roll() {
        val randomNumber = (1..6).random()
        println(randomNumber)
    }
}
  1. Esegui il codice. Dovresti vedere il risultato di un tiro di dado casuale sotto il numero di facce. Esegui il codice più volte e nota che il numero di lati rimane invariato e il valore del tiro del dado cambia.
6
4

Complimenti! Hai definito una classe Dice con una variabile sides e una funzione roll(). Nella funzione main(), hai creato una nuova istanza dell'oggetto Dice e poi hai chiamato il metodo roll() per generare un numero casuale.

Al momento stai stampando il valore di randomNumber nella funzione roll() e funziona alla grande. A volte, però, è più utile restituire il risultato di una funzione a chiunque l'abbia chiamata. Ad esempio, puoi assegnare il risultato del metodo roll() a una variabile e quindi spostare un giocatore di questo importo. Vediamo come si fa.

  1. In main() modifica la riga che contiene myFirstDice.roll(). Crea un val denominato diceRoll. Impostalo in modo che sia uguale al valore restituito dal metodo roll().
val diceRoll = myFirstDice.roll()

Questa operazione non fa ancora nulla, perché roll() non restituisce ancora nulla. Affinché questo codice funzioni come previsto, roll() deve restituire qualcosa.

Nei codelab precedenti hai imparato che devi specificare un tipo di dati per gli argomenti di input delle funzioni. Allo stesso modo, devi specificare un tipo di dati per i dati restituiti da una funzione.

  1. Modifica la funzione roll() per specificare il tipo di dati che verrà restituito. In questo caso, il numero casuale è un Int, quindi il tipo di reso è Int. La sintassi per specificare il tipo restituito è: dopo il nome della funzione, dopo le parentesi, aggiungi i due punti, uno spazio e poi la parola chiave Int per il tipo restituito della funzione. La definizione della funzione dovrebbe avere l'aspetto del codice riportato di seguito.
fun roll(): Int {
  1. Esegui questo codice. Verrà visualizzato un errore nella visualizzazione Problemi. Dice:
A ‘return'  expression is required in a function with a block body. 

Hai modificato la definizione della funzione in modo che restituisca un Int, ma il sistema segnala che il tuo

il codice non restituisce effettivamente un Int. "Corpo del blocco" o "corpo della funzione" si riferisce al codice tra le parentesi graffe di una funzione. Puoi correggere questo errore restituendo un valore da una funzione utilizzando un'istruzione return alla fine del corpo della funzione.

  1. In roll(), rimuovi l'istruzione println() e sostituiscila con un'istruzione return per randomNumber. La funzione roll() dovrebbe essere simile al codice riportato di seguito.
fun roll(): Int {
     val randomNumber = (1..6).random()
     return randomNumber
}
  1. In main() rimuovi l'istruzione di stampa per i lati del dado.
  2. Aggiungi un'istruzione per stampare il valore di sides e diceRoll in una frase informativa. La funzione main() completata dovrebbe essere simile al codice riportato di seguito.
fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
  1. Esegui il codice e l'output dovrebbe essere simile a questo.
Your 6 sided dice rolled 4!

Ecco tutto il codice finora.

fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}


class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..6).random()
        return randomNumber
    }
}

Non tutti i dadi hanno 6 facce. I dadi sono disponibili in tutte le forme e dimensioni: 4 lati, 8 lati, fino a 120 lati.

  1. Nella classe Dice, nel metodo roll(), modifica il valore 1..6 codificato in modo da utilizzare sides. In questo modo, l'intervallo e quindi il numero casuale estratto saranno sempre corretti per il numero di lati.
val randomNumber = (1..sides).random()
  1. Nella funzione main(), sotto e dopo la stampa del tiro di dado, modifica sides di myFirstDice in modo che sia impostato su 20.
myFirstDice.sides = 20
  1. Copia e incolla l'istruzione di stampa esistente riportata di seguito dopo aver modificato il numero di lati.
  2. Sostituisci la stampa di diceRoll con la stampa del risultato della chiamata al metodo roll() su myFirstDice.
println("Your ${myFirstDice.sides} sided dice has rolled a ${myFirstDice.roll()}!")

Il programma dovrebbe essere simile a questo.

fun main() {
   
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")

    myFirstDice.sides = 20
    println("Your ${myFirstDice.sides} sided dice rolled ${myFirstDice.roll()}!")
}

class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..sides).random()
        return randomNumber
    }
}
  1. Esegui il programma e dovresti visualizzare un messaggio per il dado a 6 facce e un secondo messaggio per il dado a 20 facce.
Your 6 sided dice rolled 3!
Your 20 sided dice rolled 15!

L'idea di una classe è quella di rappresentare una cosa, spesso qualcosa di fisico nel mondo reale. In questo caso, una classe Dice rappresenta un dado fisico. Nel mondo reale, i dadi non possono cambiare il numero di facce. Se vuoi un numero diverso di lati, devi prendere un altro dado. A livello di programmazione, ciò significa che, anziché modificare la proprietà sides di un'istanza dell'oggetto Dice esistente, devi creare una nuova istanza dell'oggetto dado con il numero di lati che ti serve.

In questa attività, modificherai la classe Dice in modo da poter specificare il numero di lati quando crei una nuova istanza. Modifica la definizione della classe Dice in modo che accetti un argomento per il numero di lati. È simile al modo in cui una funzione può accettare argomenti per l'input.

  1. Modifica la definizione della classe Dice per accettare un argomento di tipo intero denominato numSides. Il codice all'interno della classe non cambia.
class Dice(val numSides: Int) {
   // Code inside does not change.
}
  1. All'interno della classe Dice, elimina la variabile sides, poiché ora puoi utilizzare numSides.
  2. Inoltre, correggi l'intervallo in modo che utilizzi numSides.

La classe Dice dovrebbe essere simile a questa.

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}

Se esegui questo codice, vedrai molti errori, perché devi aggiornare main() per funzionare con le modifiche alla classe Dice.

  1. In main(), per creare myFirstDice con 6 lati, ora devi passare il numero di lati come argomento alla classe Dice, come mostrato di seguito.
    val myFirstDice = Dice(6)
  1. Nell'istruzione di stampa, modifica sides in numSides.
  2. Sotto, elimina il codice che modifica sides in 20, perché questa variabile non esiste più.
  3. Elimina anche l'affermazione println sottostante.

La funzione main() dovrebbe essere simile al codice riportato di seguito e, se la esegui, non dovrebbero verificarsi errori.

fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
  1. Dopo aver stampato il primo tiro di dadi, aggiungi il codice per creare e stampare un secondo oggetto Dice chiamato mySecondDice con 20 facce.
    val mySecondDice = Dice(20)
  1. Aggiungi un'istruzione di stampa che esegue il roll e stampa il valore restituito.
println("Your ${mySecondDice.numSides} sided dice rolled  ${mySecondDice.roll()}!")
  1. La funzione main() completata dovrebbe avere il seguente aspetto.
fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
    
    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}
  1. Esegui il programma completato e l'output dovrebbe avere il seguente aspetto.
Your 6 sided dice rolled 5!
Your 20 sided dice rolled 7!

Quando scrivi codice, la concisione è preferibile. Puoi eliminare la variabile randomNumber e restituire direttamente il numero casuale.

  1. Modifica l'istruzione return per restituire direttamente il numero casuale.
    fun roll(): Int {
        return (1..numSides).random()
    }

Nella seconda istruzione di stampa, inserisci la chiamata per ottenere il numero casuale nel modello di stringa. Puoi eliminare la variabile diceRoll facendo la stessa cosa nella prima istruzione di stampa.

  1. Chiama myFirstDice.roll() nel modello di stringa ed elimina la variabile diceRoll. Le prime due righe del codice main() ora sono simili a queste.
    val myFirstDice = Dice(6)
    println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
  1. Esegui il codice e non dovrebbe esserci alcuna differenza nell'output.

Questo è il codice finale dopo il refactoring .

fun main() {
    val myFirstDice = Dice(6)
    println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
    
    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}
fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
    
    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}
  • Chiama la funzione random() su un IntRange per generare un numero casuale: (1..6).random()
  • Le classi sono come il progetto di un oggetto. Possono avere proprietà e comportamenti, implementati come variabili e funzioni.
  • Un'istanza di una classe rappresenta un oggetto, spesso un oggetto fisico, come un dado. Puoi chiamare le azioni sull'oggetto e modificarne gli attributi.
  • Puoi passare l'input a una classe quando crei un'istanza specificando un argomento per la definizione della classe. Ad esempio: class Dice(val numSides: Int) e poi crea un'istanza con Dice(6).
  • Le funzioni possono restituire un valore. Specifica il tipo di dati da restituire nella definizione della funzione e utilizza un'istruzione return nel corpo della funzione per restituire qualcosa. Ad esempio: fun example(): Int { return 5 }

Procedi nel seguente modo:

  • Assegna alla tua classe Dice un altro attributo di colore e crea più istanze di dadi con un numero diverso di facce e colori.
  • Crea una classe Coin, dagli la possibilità di capovolgere, crea un'istanza della classe e lancia alcune monete. Come utilizzeresti la funzione random() con un intervallo per simulare il lancio di una moneta?