1. Avant de commencer
Il est courant de créer des listes pour toutes sortes de situations de la vie quotidienne : liste des tâches, liste d'invités à un événement, liste d'idées cadeaux, liste de courses, etc. En programmation aussi, les listes s'avèrent très utiles. Par exemple, une application peut contenir une liste d'articles de presse, de titres musicaux, d'événements d'agenda ou de posts sur les réseaux sociaux.
Apprendre à créer et à utiliser des listes est un concept de programmation important que vous pouvez ajouter à votre panoplie. Cela vous permettra de créer des applications plus sophistiquées.
Dans cet atelier de programmation, vous allez utiliser Kotlin Playground pour vous familiariser avec les listes en Kotlin et créer un programme permettant de commander différentes variétés de soupes de nouilles. Je parie que vous en avez déjà l'eau à la bouche.
Conditions préalables
- Vous maîtrisez Kotlin Playground pour créer et modifier des programmes Kotlin.
- Vous connaissez les concepts de programmation Kotlin exposés dans le Module 1 du cours "Principes de base d'Android en Kotlin" : fonction
main()
, arguments des fonctions et valeurs de retour, variables, types de données et opérations, et instructions de flux de contrôle. - Vous êtes capable de définir une classe Kotlin, de créer une instance d'objet à partir de celle-ci, et d'accéder à ses propriétés et méthodes.
- Vous êtes capable de créer des sous-classes et vous comprenez le concept d'héritage.
Points abordés
- Comment créer et utiliser des listes en Kotlin
- Différence entre
List
etMutableList
, et quand les utiliser - Comment effectuer une itération sur tous les éléments d'une liste et effectuer une action sur chacun d'eux
Objectifs de l'atelier
- Vous allez tester des listes et des opérations de liste dans Kotlin Playground.
- Vous allez créer un programme de commande de repas qui utilise des listes dans Kotlin Playground.
- Ce programme vous permet de créer une commande, d'y ajouter des nouilles et des légumes, puis de calculer le coût total de la commande.
Ce dont vous avez besoin
- Un ordinateur avec une connexion Internet pour accéder à Kotlin Playground
2. Présentation des listes
Dans les ateliers de programmation précédents, vous avez étudié les types de données de base en Kotlin, tels que Int
, Double
, Boolean
et String
. Ils vous permettent de stocker un certain type de valeur dans une variable. Mais que se passe-t-il si vous souhaitez stocker plusieurs valeurs ? C'est là qu'un type de données List
se révèle utile.
Une liste est un ensemble d'éléments dans un ordre spécifique. Il existe deux types de listes en Kotlin :
- Liste en lecture seule : impossible de modifier
List
après sa création. - Liste modifiable :
MutableList
peut être modifiée après sa création, ce qui signifie que vous pouvez ajouter, supprimer ou mettre à jour ses éléments.
Lorsque vous utilisez List
ou MutableList
, vous devez spécifier le type d'élément qu'il peut contenir. Par exemple, List<Int>
contient une liste d'entiers et List<String>
contient une liste de chaînes. Si vous définissez une classe Car
dans votre programme, vous pouvez créer une List<Car>
contenant une liste d'instances d'objet Car
.
Pour bien comprendre ce que sont les listes, rien ne vaut la pratique.
Créer une liste
- Ouvrez Kotlin Playground et supprimez le code existant.
- Ajoutez une fonction
main()
vide. Toutes les étapes de code suivantes seront incluses dans cette fonctionmain()
.
fun main() {
}
- Dans
main()
, créez une variable appeléenumbers
de typeList<Int>
, car elle contiendra une liste d'entiers en lecture seule. Créez uneList
à l'aide de la fonction de bibliothèque standard KotlinlistOf()
, puis transmettez les éléments de la liste en tant qu'arguments séparés par des virgules.listOf(1, 2, 3, 4, 5, 6)
renvoie une liste en lecture seule d'entiers compris entre 1 et 6.
val numbers: List<Int> = listOf(1, 2, 3, 4, 5, 6)
- Si le type de données de la variable peut être déduit (ou inféré) à partir de la valeur figurant à droite de l'opérateur d'affectation (=), vous pouvez omettre cet élément. Vous pouvez donc raccourcir cette ligne de code comme suit :
val numbers = listOf(1, 2, 3, 4, 5, 6)
- Utilisez
println()
pour imprimer la listenumbers
.
println("List: $numbers")
Pour rappel, si vous saisissez "$" dans la chaîne, ce qui suit est une expression qui sera évaluée et ajoutée à cette chaîne (voir modèles de chaîne). Cette ligne de code peut également être écrite sous la forme println("List: " + numbers).
- Récupérez la taille d'une liste à l'aide de la propriété
numbers.size
, puis imprimez-la également.
println("Size: ${numbers.size}")
- Exécutez votre programme. Vous obtenez comme résultat une liste de tous les éléments de la liste, ainsi que sa taille. Notez les crochets
[]
qui indiquent qu'il s'agit d'uneList
. Les éléments denumbers
sont placés entre crochets et séparés par des virgules. Notez également que les éléments sont dans le même ordre que lorsque vous les avez créés.
List: [1, 2, 3, 4, 5, 6] Size: 6
Éléments de la liste d'accès
La spécificité d'une liste est de vous permettre d'accéder à chacun de ses éléments par son index, qui est un nombre entier représentant la position. Voici un diagramme de la liste numbers
que nous avons créé. Il affiche chaque élément et l'index correspondant.
L'index est en fait un décalage par rapport au premier élément. Par exemple, lorsque vous dites list[2]
, vous ne demandez pas le deuxième élément de la liste, mais l'élément qui est décalé de deux positions par rapport au premier. Par conséquent, list[0]
est le premier élément (décalage de 0), list[1]
est le deuxième élément (décalage de 1), list[2]
est le troisième élément (décalage de 2), et ainsi de suite.
Ajoutez le code suivant après le code existant dans la fonction main()
. Exécutez le code après chaque étape pour vérifier que le résultat correspond à vos attentes.
- Imprimez le premier élément de la liste au niveau de l'index 0. Vous pouvez appeler la fonction
get()
avec l'index souhaité sous la formenumbers.get(0)
ou utiliser la syntaxe abrégée avec des crochets autour de l'index sous la formenumbers[0]
.
println("First element: ${numbers[0]}")
- Imprimez ensuite le deuxième élément de la liste au niveau de l'index 1.
println("Second element: ${numbers[1]}")
Les valeurs d'index valides ("indices") d'une liste vont de 0 jusqu'au dernier index, qui correspond à la taille de la liste moins 1. Autrement dit, pour votre liste numbers
, les index vont de 0 à 5.
- Imprimez le dernier élément de la liste en utilisant
numbers.size - 1
pour calculer son index, qui doit être5
. L'accès à l'élément situé au niveau du cinquième index doit renvoyer6
comme résultat.
println("Last index: ${numbers.size - 1}")
println("Last element: ${numbers[numbers.size - 1]}")
- Kotlin accepte également les opérations
first()
etlast()
sur une liste. Essayez d'appelernumbers.first()
etnumbers.last()
, et observez le résultat.
println("First: ${numbers.first()}")
println("Last: ${numbers.last()}")
Comme vous pouvez le constater, numbers.first()
renvoie le premier élément de la liste et numbers.last()
renvoie le dernier élément.
- La méthode
contains()
permet également de déterminer si un élément donné se trouve dans la liste. Par exemple, si vous disposez de la liste des noms d'employés d'une entreprise, vous pouvez utiliser la méthodecontains()
pour savoir si un nom donné figure dans cette liste.
Dans votre liste numbers
, appelez la méthode contains()
avec l'un des entiers présents. numbers.contains(4)
renvoie la valeur true
. Appelez ensuite la méthode contains()
avec un entier qui ne figure pas dans votre liste. numbers.contains(7)
renvoie false
.
println("Contains 4? ${numbers.contains(4)}")
println("Contains 7? ${numbers.contains(7)}")
- Votre code, une fois terminé, doit se présenter comme suit. Les commentaires sont facultatifs.
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6)
println("List: $numbers")
println("Size: ${numbers.size}")
// Access elements of the list
println("First element: ${numbers[0]}")
println("Second element: ${numbers[1]}")
println("Last index: ${numbers.size - 1}")
println("Last element: ${numbers[numbers.size - 1]}")
println("First: ${numbers.first()}")
println("Last: ${numbers.last()}")
// Use the contains() method
println("Contains 4? ${numbers.contains(4)}")
println("Contains 7? ${numbers.contains(7)}")
}
- Exécutez votre code. Voici la sortie.
List: [1, 2, 3, 4, 5, 6] Size: 6 First element: 1 Second element: 2 Last index: 5 Last element: 6 First: 1 Last: 6 Contains 4? true Contains 7? false
Les listes sont en lecture seule
- Supprimez le code de Kotlin Playground et remplacez-le par le code suivant. La liste
colors
est initialisée sur une liste de trois couleurs représentée parStrings
.
fun main() {
val colors = listOf("green", "orange", "blue")
}
- Pour rappel, vous ne pouvez ni ajouter ni modifier d'éléments dans une
List
en lecture seule. Voyez ce qui se passe lorsque vous essayez d'ajouter un élément à la liste ou de modifier un élément de la liste en le définissant sur une nouvelle valeur.
colors.add("purple")
colors[0] = "yellow"
- Vous exécutez votre code et plusieurs messages d'erreur sont renvoyés. En substance, les erreurs indiquent que la méthode
add()
n'existe pas pourList
et que vous n'êtes pas autorisé à modifier la valeur d'un élément.
- Supprimez le code incorrect.
Vous avez pu constater qu'il n'était pas possible de modifier une liste en lecture seule. Cependant, il existe plusieurs opérations qui n'ont pas d'incidence sur les listes, mais qui renvoient une nouvelle liste. C'est le cas de reversed()
et sorted()
. La fonction reversed()
renvoie une nouvelle liste dans laquelle les éléments sont dans l'ordre inverse. sorted()
renvoie une nouvelle liste dans laquelle les éléments sont triés par ordre croissant.
- Ajoutez du code pour inverser la liste
colors
. Imprimez la sortie. Il s'agit d'une nouvelle liste contenant les éléments decolors
dans l'ordre inverse. - Ajoutez une deuxième ligne de code pour imprimer la
list
d'origine. Vous pourrez ainsi constater que la liste d'origine n'a pas changé.
println("Reversed list: ${colors.reversed()}")
println("List: $colors")
- Voici la sortie des deux listes imprimées.
Reversed list: [blue, orange, green] List: [green, orange, blue]
- Ajoutez du code pour renvoyer une version classée d'une
List
à l'aide de la fonctionsorted()
.
println("Sorted list: ${colors.sorted()}")
Le résultat est une nouvelle liste de couleurs classées par ordre alphabétique. Super !
Sorted list: [blue, green, orange]
- Vous pouvez également exécuter la fonction
sorted()
sur une liste de nombres non triés.
val oddNumbers = listOf(5, 3, 7, 1)
println("List: $oddNumbers")
println("Sorted list: ${oddNumbers.sorted()}")
List: [5, 3, 7, 1] Sorted list: [1, 3, 5, 7]
Vous pouvez maintenant constater à quel point il est utile de pouvoir créer des listes. Toutefois, il serait intéressant de pouvoir modifier la liste après sa création. Nous allons donc nous pencher maintenant sur les listes modifiables.
3. Présentation des listes modifiables
Les listes de ce type peuvent être modifiées après leur création. Vous pouvez ajouter, supprimer ou modifier des éléments. Vous pouvez également effectuer toutes les opérations réalisables sur les listes en lecture seule. Les listes modifiables sont de type MutableList
. Vous pouvez les créer en appelant mutableListOf()
.
Créer une MutableList
- Supprimez le code existant dans
main()
. - Dans la fonction
main()
, créez une liste modifiable vide et attribuez-la à une variableval
appeléeentrees
.
val entrees = mutableListOf()
L'erreur suivante se produit si vous essayez d'exécuter votre code.
Not enough information to infer type variable T
Comme indiqué précédemment, lorsque vous créez une MutableList
ou une List
, Kotlin tente de déduire le type des éléments contenus dans la liste à partir des arguments transmis. Par exemple, si vous écrivez listOf("noodles")
, Kotlin en déduit que vous souhaitez créer une liste de String
. Lorsque vous initialisez une liste vide (c'est-à-dire sans éléments), Kotlin ne peut pas déduire le type des éléments. Vous devez donc l'indiquer explicitement. Pour ce faire, ajoutez le type entre chevrons, immédiatement après mutableListOf
ou listOf
. Dans la documentation, cela peut être représenté sous la forme <T>
, où T
correspond au paramètre de type.
- Corrigez la déclaration de variable pour indiquer que vous souhaitez créer une liste modifiable de type
String
.
val entrees = mutableListOf<String>()
Vous pouvez également corriger l'erreur en spécifiant, à l'avance, le type de données de la variable.
val entrees: MutableList<String> = mutableListOf()
- Imprimez la liste.
println("Entrees: $entrees")
- La sortie affiche
[]
pour une liste vide.
Entrees: []
Ajouter des éléments à une liste
L'utilisation de listes modifiables devient intéressante lorsque vous ajoutez, supprimez et mettez à jour des éléments.
- Ajoutez
"noodles"
à la liste avecentrees.add("noodles").
. La fonctionadd()
renvoietrue
si l'élément a bien été ajouté à la liste. Sinon,false
est renvoyé. - Imprimez la liste pour vérifier que
"noodles"
a bien été ajouté.
println("Add noodles: ${entrees.add("noodles")}")
println("Entrees: $entrees")
Voici la sortie :
Add noodles: true Entrees: [noodles]
- Ajoutez un autre élément
"spaghetti"
à la liste.
println("Add spaghetti: ${entrees.add("spaghetti")}")
println("Entrees: $entrees")
La liste entrees
obtenue contient maintenant deux éléments.
Add spaghetti: true Entrees: [noodles, spaghetti]
Au lieu d'ajouter des éléments un par un à l'aide de add()
, vous pouvez en ajouter plusieurs à la fois à l'aide de addAll()
et transmettre une liste.
- Créez une liste de
moreItems
. Comme aucune modification ne sera nécessaire, définissez-la commeval
et faites-en sorte qu'elle ne soit pas modifiable.
val moreItems = listOf("ravioli", "lasagna", "fettuccine")
- Utilisez
addAll()
pour ajouter tous les éléments de la nouvelle liste àentrees
. Imprimez la liste obtenue.
println("Add list: ${entrees.addAll(moreItems)}")
println("Entrees: $entrees")
La sortie indique que la liste a bien été ajoutée. La liste entrees
contient désormais un total de cinq éléments.
Add list: true Entrees: [noodles, spaghetti, ravioli, lasagna, fettuccine]
- Essayez maintenant d'ajouter un nombre à cette liste.
entrees.add(10)
Cette opération échoue et une erreur est renvoyée :
The integer literal does not conform to the expected type String
Cela est dû au fait que vous essayez d'ajouter un Int
alors que la liste entrees
attend des éléments de type String
. N'oubliez pas d'ajouter uniquement des éléments dont le type de données est adapté à une liste. Sinon, une erreur de compilation sera renvoyée. C'est l'une des méthodes utilisées par Kotlin pour garantir la sûreté de typage de votre code.
- Supprimez la ligne de code incorrecte afin que votre code soit compilé.
Supprimer des éléments d'une liste
- Appelez
remove()
pour supprimer"spaghetti"
de la liste. Imprimez de nouveau la liste.
println("Remove spaghetti: ${entrees.remove("spaghetti")}")
println("Entrees: $entrees")
- La suppression de
"spaghetti"
renvoie la valeur "true", car l'élément était présent dans la liste et a pu être supprimé. Il ne reste plus que quatre éléments dans la liste.
Remove spaghetti: true Entrees: [noodles, ravioli, lasagna, fettuccine]
- Que se passe-t-il si vous essayez de supprimer un élément qui ne figure pas dans la liste ? Essayez de supprimer
"rice"
de la liste avecentrees.remove("rice")
.
println("Remove item that doesn't exist: ${entrees.remove("rice")}")
println("Entrees: $entrees")
La méthode remove()
renvoie false
, car l'élément n'existe pas et ne peut donc pas être supprimé. La liste reste inchangée et ne contient toujours que quatre éléments. Sortie :
Remove item that doesn't exist: false Entrees: [noodles, ravioli, lasagna, fettuccine]
- Vous pouvez également spécifier l'index de l'élément à supprimer. Utilisez
removeAt()
pour supprimer l'élément au niveau de l'index0
.
println("Remove first element: ${entrees.removeAt(0)}")
println("Entrees: $entrees")
La valeur renvoyée pour removeAt(0)
est le premier élément ("noodles"
) qui a été supprimé de la liste. Il reste à présent trois éléments dans la liste entrees
.
Remove first element: noodles Entrees: [ravioli, lasagna, fettuccine]
- Si vous souhaitez effacer toute la liste, vous pouvez appeler
clear()
.
entrees.clear()
println("Entrees: $entrees")
La sortie affiche maintenant une liste vide.
Entrees: []
- Kotlin vous permet de vérifier si une liste est vide à l'aide de la fonction
isEmpty()
. Essayez d'imprimerentrees.isEmpty().
println("Empty? ${entrees.isEmpty()}")
La valeur "true" doit être renvoyée, car la liste est actuellement vide.
Empty? true
La méthode isEmpty()
est utile si vous souhaitez effectuer une opération sur une liste ou accéder à un élément donné, tout en vous assurant d'abord que la liste n'est pas vide.
Voici le code que vous avez écrit pour les listes modifiables. Les commentaires sont facultatifs.
fun main() {
val entrees = mutableListOf<String>()
println("Entrees: $entrees")
// Add individual items using add()
println("Add noodles: ${entrees.add("noodles")}")
println("Entrees: $entrees")
println("Add spaghetti: ${entrees.add("spaghetti")}")
println("Entrees: $entrees")
// Add a list of items using addAll()
val moreItems = listOf("ravioli", "lasagna", "fettuccine")
println("Add list: ${entrees.addAll(moreItems)}")
println("Entrees: $entrees")
// Remove an item using remove()
println("Remove spaghetti: ${entrees.remove("spaghetti")}")
println("Entrees: $entrees")
println("Remove item that doesn't exist: ${entrees.remove("rice")}")
println("Entrees: $entrees")
// Remove an item using removeAt() with an index
println("Remove first element: ${entrees.removeAt(0)}")
println("Entrees: $entrees")
// Clear out the list
entrees.clear()
println("Entrees: $entrees")
// Check if the list is empty
println("Empty? ${entrees.isEmpty()}")
}
4. Lire une liste en boucle
Pour effectuer une opération sur chaque élément d'une liste, vous pouvez la lire en boucle (on parle également d'itération de liste). Vous pouvez utiliser les boucles avec Lists
et MutableLists
.
Boucles While
while
est un type de boucle. Une boucle while
commence par le mot clé while
en langage Kotlin. Elle contient un bloc de code (entre accolades) qui est exécuté de façon répétée, à condition que l'expression entre parenthèses soit vraie. Pour éviter que le code s'exécute indéfiniment (c'est ce que l'on appelle une boucle infinie), le bloc de code doit contenir une logique qui modifie la valeur de l'expression. Ainsi, au final, cette expression sera fausse et vous cesserez d'exécuter la boucle. À ce stade, vous quittez la boucle while
et vous continuez d'exécuter le code qui la suit.
while (expression) {
// While the expression is true, execute this code block
}
Utilisez une boucle while
pour itérer une liste. Créez une variable pour effectuer le suivi de l'index
observé actuellement dans la liste. La variable index
continue d'augmenter de 1 chaque fois que vous atteignez le dernier index de la liste, après quoi vous quittez la boucle.
- Supprimez le code existant dans Kotlin Playground et utilisez une fonction
main()
vide. - Supposons que vous organisiez une fête. Créez une liste où chaque élément représente le nombre de personnes ayant répondu pour chaque famille. La première famille a indiqué que deux personnes assisteraient à la fête. La deuxième famille a indiqué que quatre personnes seraient présentes, et ainsi de suite.
val guestsPerFamily = listOf(2, 4, 1, 3)
- Déterminez le nombre total d'invités. Écrivez une boucle pour obtenir la réponse. Créez un
var
pour le nombre total d'invités et initialisez-le sur0
.
var totalGuests = 0
- Initialisez une
var
pour la variableindex
, comme décrit précédemment.
var index = 0
- Écrivez une boucle
while
pour itérer la liste. La condition est de continuer à exécuter le bloc de code, à condition que la valeurindex
soit inférieure à la taille de la liste.
while (index < guestsPerFamily.size) {
}
- Dans la boucle, récupérez l'élément de la liste au niveau de l'
index
actuel et ajoutez-le à la variable du nombre total d'invités. N'oubliez pas quetotalGuests += guestsPerFamily[index]
est identique àtotalGuests = totalGuests + guestsPerFamily[index].
Comme vous pouvez le voir, la dernière ligne de la boucle incrémente la variable index
de 1 à l'aide de index++
, de sorte que l'itération suivante de la boucle recherche la famille suivante dans la liste.
while (index < guestsPerFamily.size) {
totalGuests += guestsPerFamily[index]
index++
}
- Après la boucle
while
, vous pouvez imprimer le résultat.
while ... {
...
}
println("Total Guest Count: $totalGuests")
- Exécutez le programme. Vous obtenez la sortie suivante. Vous pouvez vérifier qu'il s'agit de la bonne réponse en ajoutant manuellement les chiffres dans la liste.
Total Guest Count: 10
Voici l'extrait de code complet :
val guestsPerFamily = listOf(2, 4, 1, 3)
var totalGuests = 0
var index = 0
while (index < guestsPerFamily.size) {
totalGuests += guestsPerFamily[index]
index++
}
println("Total Guest Count: $totalGuests")
Avec une boucle while
, vous deviez écrire du code pour créer une variable permettant d'effectuer le suivi de l'index, obtenir l'élément au niveau de l'index dans la liste et mettre à jour cette variable d'index. Il existe une méthode encore plus rapide et concise pour itérer une liste : utiliser des boucles for
.
Boucles For
for
est un autre type de boucle qui simplifie considérablement la lecture d'une liste en boucle. Cette boucle commence par le mot clé for
en Kotlin, suivi du bloc de code entre accolades. La condition d'exécution du bloc de code est indiquée entre parenthèses.
for (number in numberList) {
// For each element in the list, execute this code block
}
Dans cet exemple, la variable number
est définie sur la valeur du premier élément de numberList
, et le bloc de code est exécuté. Ensuite, la variable number
est automatiquement mise à jour pour devenir l'élément suivant de numberList
et le bloc de code est de nouveau exécuté. Cette opération se répète pour chaque élément de la liste, jusqu'à atteindre la fin de numberList
.
- Supprimez le code existant dans Kotlin Playground et remplacez-le par le code suivant :
fun main() {
val names = listOf("Jessica", "Henry", "Alicia", "Jose")
}
- Ajoutez une boucle
for
pour imprimer tous les éléments de la listenames
.
for (name in names) {
println(name)
}
C'est beaucoup plus simple que de devoir écrire cela en tant que boucle while
.
- Voici la sortie :
Jessica Henry Alicia Jose
Une opération courante sur les listes consiste à effectuer une action sur chaque élément.
- Modifiez la boucle de manière à imprimer également le nombre de caractères qui composent le nom de cette personne. Conseil : vous pouvez utiliser la propriété
length
d'uneString
pour connaître le nombre de caractères de cetteString
.
val names = listOf("Jessica", "Henry", "Alicia", "Jose")
for (name in names) {
println("$name - Number of characters: ${name.length}")
}
Sortie :
Jessica - Number of characters: 7 Henry - Number of characters: 5 Alicia - Number of characters: 6 Jose - Number of characters: 4
Le code de la boucle n'a pas modifié l'élément List
d'origine. Seul le contenu imprimé a été affecté.
Ce qui est vraiment génial, c'est que vous pouvez écrire des instructions pour les actions à effectuer pour un élément de liste et que le code est exécuté pour tous les éléments. Une boucle peut vous dispenser de la saisie d'une grande quantité de code répétitif.
Maintenant que vous vous êtes essayé à la création et l'utilisation de listes et de listes modifiables, et que vous avez découvert les boucles, il est temps d'appliquer ces connaissances à un exemple de cas d'utilisation.
5. Mise en pratique
Lorsque vous commandez un repas dans un restaurant près de chez vous, la commande en question contient généralement plusieurs articles. L'utilisation de listes est idéale pour stocker des informations sur une commande. Vous exploitez également vos connaissances des classes et de l'héritage pour créer un programme Kotlin plus efficace et évolutif, au lieu de placer tout le code dans la fonction main()
.
Pour la série de tâches suivante, vous allez créer un programme Kotlin permettant de commander différentes combinaisons d'aliments.
Examinons d'abord cet exemple de sortie du code final. Pourriez-vous réfléchir aux types de classes à créer pour organiser toutes ces données ?
Order #1 Noodles: $10 Total: $10 Order #2 Noodles: $10 Vegetables Chef's Choice: $5 Total: $15
Voici quelques observations concernant la sortie ci-dessous :
- Il y a une liste de commandes
- Chaque commande est associée à un numéro
- Chaque commande peut contenir une liste d'aliments (nouilles, légumes, etc.)
- Chaque article a un prix
- Chaque commande a un prix total, qui correspond à la somme des prix de chaque article
Vous pouvez créer une classe pour représenter un Order
et une classe pour chaque aliment, comme Noodles
ou Vegetables
. Vous pouvez également observer que Noodles
et Vegetables
présentent quelques similitudes, car ce sont des aliments et qu'ils ont chacun un prix. Vous pouvez envisager de créer une classe Item
avec des propriétés partagées dont les classes Noodle
et Vegetable
peuvent hériter. Cela vous évite de devoir dupliquer la logique dans les classes Noodle
et Vegetable
.
- Le code de démarrage suivant est fourni. Il arrive fréquemment que les développeurs professionnels doivent lire le code d'autres personnes, par exemple lorsqu'ils rejoignent un nouveau projet ou complètent une fonctionnalité créée par quelqu'un d'autre. Être capable de lire et de comprendre le code est une compétence importante.
Prenez le temps d'examiner ce code et de comprendre ce qui se passe. Copiez et collez ce code dans Kotlin Playground, puis exécutez-le. Veillez à supprimer tout code existant dans Kotlin Playground avant de coller ce nouveau code. Examinez la sortie et voyez si cela vous aide à mieux comprendre le code.
open class Item(val name: String, val price: Int)
class Noodles : Item("Noodles", 10)
class Vegetables : Item("Vegetables", 5)
fun main() {
val noodles = Noodles()
val vegetables = Vegetables()
println(noodles)
println(vegetables)
}
- Vous devriez obtenir une sortie semblable à ceci :
Noodles@5451c3a8 Vegetables@76ed5528
Voici une explication plus détaillée du code. Il y a tout d'abord une classe appelée Item
, dans laquelle le constructeur accepte deux paramètres : un name
pour l'élément (String, par exemple) et un price
(sous la forme d'un nombre entier). Les deux propriétés ne sont pas modifiées après avoir été transmises. Elles sont donc marquées comme val
. Comme Item
est une classe parente à partir de laquelle les sous-classes sont étendues, la classe est marquée avec le mot clé open
.
Le constructeur de classe Noodles
n'accepte aucun paramètre, mais s'étend à partir de Item
et appelle le constructeur de super-classe en transmettant "Noodles"
comme nom et un prix de 10. La classe Vegetables
est similaire, mais elle appelle le constructeur de super-classe avec "Vegetables"
et un prix de 5.
La fonction main()
initialise les nouvelles instances d'objet des classes Noodles
et Vegetables
, puis les imprime dans la sortie.
Ignorer la méthode toString()
Lorsque vous imprimez une instance d'objet dans la sortie, la méthode toString()
de l'objet est appelée. En langage Kotlin, chaque classe hérite automatiquement de la méthode toString()
. L'implémentation par défaut de cette méthode renvoie simplement le type d'objet avec une adresse mémoire pour l'instance. Vous devez ignorer toString()
pour renvoyer quelque chose de plus explicite et convivial que Noodles@5451c3a8
et Vegetables@76ed5528
.
- Dans la classe
Noodles
, ignorez la méthodetoString()
et faites en sorte qu'elle renvoiename
. N'oubliez pas queNoodles
hérite de la propriéténame
de sa classe parenteItem
.
class Noodles : Item("Noodles", 10) {
override fun toString(): String {
return name
}
}
- Répétez la même opération pour la classe
Vegetables
.
class Vegetables() : Item("Vegetables", 5) {
override fun toString(): String {
return name
}
}
- Exécutez votre code. Vous obtenez alors un meilleur résultat :
Noodles
Vegetables
À l'étape suivante, vous allez modifier le constructeur de classe Vegetables
afin d'intégrer certains paramètres, puis mettre à jour la méthode toString()
pour tenir compte de ces informations supplémentaires.
Personnaliser les légumes dans une commande
Pour augmenter l'intérêt des clients pour les soupes de nouilles, vous pouvez inclure différents légumes dans vos commandes.
- Dans la fonction
main()
, au lieu d'initialiser une instanceVegetables
sans argument d'entrée, transmettez des types de légumes spécifiques que le client apprécie.
fun main() {
...
val vegetables = Vegetables("Cabbage", "Sprouts", "Onion")
...
}
Si vous essayez de compiler votre code maintenant, l'erreur suivante est renvoyée :
Too many arguments for public constructor Vegetables() defined in Vegetables
Vous transmettez à présent trois arguments String au constructeur de classe Vegetables
. Vous devrez donc modifier la classe Vegetables
.
- Mettez à jour l'en-tête de classe
Vegetables
pour qu'il intègre trois paramètres de chaîne, comme indiqué dans le code suivant :
class Vegetables(val topping1: String,
val topping2: String,
val topping3: String) : Item ("Vegetables", 5) {
- Une nouvelle compilation de votre code est effectuée. Cependant, cette solution ne fonctionne que si vos clients souhaitent toujours commander exactement trois légumes. Si un client souhaite commander un ou cinq légumes, il ne pourra pas le faire.
- Au lieu d'utiliser une propriété pour chaque légume, vous pouvez résoudre le problème en acceptant une liste de légumes (de n'importe quelle taille) dans le constructeur de la classe
Vegetables
.List
ne doit contenir queStrings
. Le paramètre d'entrée est donc de typeList<String>
.
class Vegetables(val toppings: List<String>) : Item("Vegetables", 5) {
Ce n'est certes pas la solution la plus judicieuse car, dans main()
, vous devez modifier votre code pour créer une liste de garnitures avant de la transmettre au constructeur Vegetables
.
Vegetables(listOf("Cabbage", "Sprouts", "Onion"))
Il existe une meilleure solution au problème.
- En langage Kotlin, le modificateur
vararg
vous permet de transmettre un nombre variable d'arguments du même type à une fonction ou un constructeur. De cette façon, vous pouvez fournir les différents légumes en tant que chaînes individuelles plutôt que sous la forme d'une liste.
Modifiez la définition de classe de Vegetables
pour utiliser un vararg
toppings
de type String
.
class Vegetables(vararg val toppings: String) : Item("Vegetables", 5) {
- Ce code dans la fonction
main()
est maintenant opérationnel. Vous pouvez créer une instanceVegetables
en transmettant un nombre indéfini de chaînes de garniture.
fun main() {
...
val vegetables = Vegetables("Cabbage", "Sprouts", "Onion")
...
}
- Modifiez maintenant la méthode
toString()
de la classeVegetables
afin qu'elle renvoie uneString
qui mentionne également les garnitures au format suivant :Vegetables Cabbage, Sprouts, Onion
.
Commencez par le nom de l'élément (Vegetables
). Utilisez ensuite la méthode joinToString()
pour joindre toutes les garnitures dans une seule chaîne. Joignez ces deux parties à l'aide de l'opérateur +
en les séparant par un espace.
class Vegetables(vararg val toppings: String) : Item("Vegetables", 5) {
override fun toString(): String {
return name + " " + toppings.joinToString()
}
}
- Exécutez votre programme. La sortie doit se présenter comme suit :
Noodles Vegetables Cabbage, Sprouts, Onion
- Lorsque vous écrivez des programmes, vous devez prendre en compte toutes les entrées possibles. En l'absence d'arguments d'entrée dans le constructeur
Vegetables
, traitez la méthodetoString()
d'une manière plus conviviale.
Puisque le client veut des légumes, mais qu'il n'a pas dit lesquels, une solution consiste à lui proposer par défaut la sélection du chef.
Mettez à jour la méthode toString()
pour renvoyer Vegetables Chef's Choice
si aucune garniture n'est transmise. Utilisez la méthode isEmpty()
que vous avez étudiée précédemment.
override fun toString(): String {
if (toppings.isEmpty()) {
return "$name Chef's Choice"
} else {
return name + " " + toppings.joinToString()
}
}
- Mettez à jour la fonction
main()
pour tester les deux possibilités de création d'une instanceVegetables
, c'est-à-dire sans aucun argument de constructeur et avec plusieurs arguments.
fun main() {
val noodles = Noodles()
val vegetables = Vegetables("Cabbage", "Sprouts", "Onion")
val vegetables2 = Vegetables()
println(noodles)
println(vegetables)
println(vegetables2)
}
- Vérifiez que le résultat est conforme à vos attentes.
Noodles Vegetables Cabbage, Sprouts, Onion Vegetables Chef's Choice
Créer une commande
Maintenant que vous disposez d'un choix d'aliments, vous pouvez créer une commande. Encapsulez la logique d'une commande dans une classe Order
de votre programme.
- Réfléchissez aux propriétés et méthodes qui sont pertinentes pour la classe
Order
. Si cela peut vous aider, voici à nouveau un exemple de sortie du code final.
Order #1 Noodles: $10 Total: $10 Order #2 Noodles: $10 Vegetables Chef's Choice: $5 Total: $15 Order #3 Noodles: $10 Vegetables Carrots, Beans, Celery: $5 Total: $15 Order #4 Noodles: $10 Vegetables Cabbage, Onion: $5 Total: $15 Order #5 Noodles: $10 Noodles: $10 Vegetables Spinach: $5 Total: $25
- Vous avez peut-être pensé aux éléments suivants :
Classe de commande
Propriétés : numéro de commande, liste d'éléments
Méthodes : ajouter un élément, ajouter plusieurs éléments, imprimer le récapitulatif de la commande (y compris le prix)
- En commençant par les propriétés, quel devrait être le type de données de chacune d'elles ? Doivent-elles être publiques ou privées pour la classe ? Doivent-elles être transmises en tant qu'arguments ou définies dans la classe ?
- Nous vous proposons ici une solution, mais sachez qu'il existe plusieurs façons de faire. Créez une
Order
class
ayant un paramètre de constructeurorderNumber
entier.
class Order(val orderNumber: Int)
- Étant donné que vous ne connaissez peut-être pas à l'avance tous les éléments de la commande, n'exigez pas que la liste d'éléments soit transmise en tant qu'argument. Au lieu de cela, vous pouvez la déclarer en tant que variable de classe de niveau supérieur, puis l'initialiser en tant que
MutableList
vide pouvant contenir des éléments de typeItem
. Marquez la variableprivate
afin que seule cette classe puisse modifier directement cette liste d'éléments. Cela empêchera toute modification inattendue de la liste par du code en dehors de cette classe.
class Order(val orderNumber: Int) {
private val itemList = mutableListOf<Item>()
}
- Vous pouvez également ajouter les méthodes à la définition de classe. N'hésitez pas à choisir des noms pertinents pour chaque méthode. Pour le moment, vous pouvez laisser la logique d'implémentation vide pour chaque méthode. Déterminez également quels arguments de fonction et valeurs de retour sont nécessaires.
class Order(val orderNumber: Int) {
private val itemList = mutableListOf<Item>()
fun addItem(newItem: Item) {
}
fun addAll(newItems: List<Item>) {
}
fun print() {
}
}
- Comme la méthode
addItem()
semble être la plus simple, commencez par implémenter cette fonction. Elle utilise un nouvelItem
que la méthode doit ajouter àitemList
.
fun addItem(newItem: Item) {
itemList.add(newItem)
}
- Implémentez ensuite la méthode
addAll()
. Elle utilise une liste d'éléments en lecture seule. Ajoutez tous ces éléments à la liste interne.
fun addAll(newItems: List<Item>) {
itemList.addAll(newItems)
}
- Ensuite, implémentez la méthode
print()
qui imprime un récapitulatif de tous les éléments et leurs prix dans la sortie, ainsi que le prix total de la commande.
Commencez par imprimer le numéro de commande. Utilisez ensuite une boucle pour parcourir tous les éléments de la liste. Imprimez chaque élément et son prix. Conservez également le prix total provisoire et continuez à l'incrémenter à mesure que vous itérez la liste. Imprimez le prix total à la fin. Essayez d'implémenter cette logique vous-même. Si vous avez besoin d'aide, consultez la solution ci-dessous.
Vous pouvez inclure le symbole monétaire pour faciliter la lecture de la sortie. Voici un moyen d'implémenter la solution. Ce code utilise le symbole "$", mais n'hésitez pas à utiliser le symbole de votre devise locale.
fun print() {
println("Order #${orderNumber}")
var total = 0
for (item in itemList) {
println("${item}: $${item.price}")
total += item.price
}
println("Total: $${total}")
}
Pour chaque item
de itemList
, imprimez l'item
(ce qui déclenche l'appel de toString()
sur item
) suivi du price
de l'article. Avant la boucle, initialisez également une variable entière total
sur 0. Continuez ensuite à incrémenter le total en ajoutant le prix de l'élément actuel à total
.
Créer des commandes
- Testez votre code en créant des instances
Order
dans la fonctionmain()
. Supprimez d'abord le contenu actuel de votre fonctionmain()
. - Vous pouvez utiliser ces exemples de commande ou créer les vôtres. Essayez différentes combinaisons d'éléments dans les commandes en veillant à tester tous les chemins dans votre code. Par exemple, testez les méthodes
addItem()
etaddAll()
dans la classeOrder
, créez des instancesVegetables
avec et sans arguments, etc.
fun main() {
val order1 = Order(1)
order1.addItem(Noodles())
order1.print()
println()
val order2 = Order(2)
order2.addItem(Noodles())
order2.addItem(Vegetables())
order2.print()
println()
val order3 = Order(3)
val items = listOf(Noodles(), Vegetables("Carrots", "Beans", "Celery"))
order3.addAll(items)
order3.print()
}
- La sortie du code ci-dessus doit se présenter comme suit. Assurez-vous que le prix total augmente correctement.
Order #1 Noodles: $10 Total: $10 Order #2 Noodles: $10 Vegetables Chef's Choice: $5 Total: $15 Order #3 Noodles: $10 Vegetables Carrots, Beans, Celery: $5 Total: $15
Bravo ! Maintenant, cela ressemble vraiment à une commande de repas.
6. Améliorer votre code
Conserver une liste de commandes
Si vous deviez créer un programme destiné à être utilisé dans un restaurant de nouilles, il serait judicieux de conserver une liste de toutes les commandes passées par les clients.
- Créez une liste pour y stocker toutes les commandes. S'agit-il d'une liste en lecture seule ou d'une liste modifiable ?
- Ajoutez ce code à la fonction
main()
. Initialisez la liste pour qu'elle soit vide au début. Ensuite, une fois chaque commande créée, ajoutez-la à la liste.
fun main() {
val ordersList = mutableListOf<Order>()
val order1 = Order(1)
order1.addItem(Noodles())
ordersList.add(order1)
val order2 = Order(2)
order2.addItem(Noodles())
order2.addItem(Vegetables())
ordersList.add(order2)
val order3 = Order(3)
val items = listOf(Noodles(), Vegetables("Carrots", "Beans", "Celery"))
order3.addAll(items)
ordersList.add(order3)
}
Étant donné que les commandes sont ajoutées au fil du temps, la liste doit être une MutableList
de type Order
. Utilisez ensuite la méthode add()
sur MutableList
pour ajouter chaque commande.
- Une fois que vous disposez d'une liste de commandes, vous pouvez utiliser une boucle pour imprimer chacune d'elles. Imprimez une ligne vide entre les commandes pour faciliter la lecture de la sortie.
fun main() {
val ordersList = mutableListOf<Order>()
...
for (order in ordersList) {
order.print()
println()
}
}
Cela supprime le code en double dans notre fonction main()
et rend le code plus facile à lire. La sortie doit être identique à celle obtenue précédemment.
Implémenter un schéma Builder pour les commandes
Pour que votre code Kotlin soit plus concis, utilisez le schéma Builder lors de la création de commandes. Builder est un schéma de conception en programmation qui vous permet de créer un objet complexe, étape par étape.
- Au lieu de renvoyer
Unit
(ou rien) à partir des méthodesaddItem()
etaddAll()
de la classeOrder
, renvoyez laOrder
modifiée. Kotlin fournit le mot cléthis
pour référencer l'instance d'objet actuelle. Dans les méthodesaddItem()
etaddAll()
, la valeurOrder
actuelle est renvoyée avecthis
.
fun addItem(newItem: Item): Order {
itemList.add(newItem)
return this
}
fun addAll(newItems: List<Item>): Order {
itemList.addAll(newItems)
return this
}
- Dans la fonction
main()
, vous pouvez désormais enchaîner les appels, comme indiqué dans le code suivant. Ce code crée unOrder
et tire parti du modèle Builder.
val order4 = Order(4).addItem(Noodles()).addItem(Vegetables("Cabbage", "Onion"))
ordersList.add(order4)
Order(4)
renvoie une instance Order
, sur laquelle vous pouvez ensuite appeler addItem(Noodles())
. La méthode addItem()
renvoie la même instance Order
(avec le nouvel état) et vous pouvez à nouveau appeler addItem()
sur celle-ci avec des légumes. Le résultat Order
renvoyé peut être stocké dans la variable order4
.
Le code actuel permettant de créer Orders
fonctionne toujours et peut donc être conservé tel quel. Bien qu'il ne soit pas obligatoire d'enchaîner ces appels, il s'agit d'une pratique courante et recommandée qui tire parti de la valeur renvoyée par la fonction.
- À ce stade, il n'est même pas nécessaire de stocker la commande dans une variable. Dans la fonction
main()
(avant la boucle finale utilisée pour imprimer les commandes), créez directement uneOrder
et ajoutez-la àorderList
. Le code est également plus facile à lire si chaque appel de méthode est placé sur sa propre ligne.
ordersList.add(
Order(5)
.addItem(Noodles())
.addItem(Noodles())
.addItem(Vegetables("Spinach")))
- Exécutez votre code. Voici la sortie attendue :
Order #1 Noodles: $10 Total: $10 Order #2 Noodles: $10 Vegetables Chef's Choice: $5 Total: $15 Order #3 Noodles: $10 Vegetables Carrots, Beans, Celery: $5 Total: $15 Order #4 Noodles: $10 Vegetables Cabbage, Onion: $5 Total: $15 Order #5 Noodles: $10 Noodles: $10 Vegetables Spinach: $5 Total: $25
Félicitations, vous avez terminé cet atelier de programmation !
Vous savez maintenant à quel point il peut être utile de stocker des données dans des listes, de modifier des listes et de les lire en boucle. Appliquez ces connaissances dans le contexte d'une application Android pour afficher une liste de données à l'écran dans le prochain atelier de programmation.
7. Code de solution
Voici le code de solution pour les classes Item
, Noodles
, Vegetables
et Order
. La fonction main()
montre également comment utiliser ces classes. Il existe plusieurs façons d'implémenter ce programme. Votre code peut donc être légèrement différent.
open class Item(val name: String, val price: Int)
class Noodles : Item("Noodles", 10) {
override fun toString(): String {
return name
}
}
class Vegetables(vararg val toppings: String) : Item("Vegetables", 5) {
override fun toString(): String {
if (toppings.isEmpty()) {
return "$name Chef's Choice"
} else {
return name + " " + toppings.joinToString()
}
}
}
class Order(val orderNumber: Int) {
private val itemList = mutableListOf<Item>()
fun addItem(newItem: Item): Order {
itemList.add(newItem)
return this
}
fun addAll(newItems: List<Item>): Order {
itemList.addAll(newItems)
return this
}
fun print() {
println("Order #${orderNumber}")
var total = 0
for (item in itemList) {
println("${item}: $${item.price}")
total += item.price
}
println("Total: $${total}")
}
}
fun main() {
val ordersList = mutableListOf<Order>()
// Add an item to an order
val order1 = Order(1)
order1.addItem(Noodles())
ordersList.add(order1)
// Add multiple items individually
val order2 = Order(2)
order2.addItem(Noodles())
order2.addItem(Vegetables())
ordersList.add(order2)
// Add a list of items at one time
val order3 = Order(3)
val items = listOf(Noodles(), Vegetables("Carrots", "Beans", "Celery"))
order3.addAll(items)
ordersList.add(order3)
// Use builder pattern
val order4 = Order(4)
.addItem(Noodles())
.addItem(Vegetables("Cabbage", "Onion"))
ordersList.add(order4)
// Create and add order directly
ordersList.add(
Order(5)
.addItem(Noodles())
.addItem(Noodles())
.addItem(Vegetables("Spinach"))
)
// Print out each order
for (order in ordersList) {
order.print()
println()
}
}
8. Résumé
Kotlin propose des fonctionnalités qui vous permettent de gérer et de manipuler plus facilement des collections de données via la bibliothèque standard Kotlin. Une collection peut être définie comme un nombre d'objets ayant le même type de données. Il existe différents types de collection de base en langage Kotlin : les listes, les ensembles et les cartes. Cet atelier de programmation porte plus spécifiquement sur les listes. Vous en apprendrez davantage sur les ensembles et les cartes dans les prochains ateliers.
- Une liste est un ensemble ordonné d'éléments d'un certain type ; une liste de
Strings.
, par exemple. - L'index est la position entière qui reflète la position de l'élément (par exemple,
myList[2]
). - Dans une liste, le premier élément se trouve au niveau de l'index 0 (par exemple,
myList[0]
) et le dernier au niveau demyList.size-1
(par exemple,myList[myList.size-1]
oumyList.last()
). - Il existe deux types de listes :
List
etMutableList.
- Une
List
est en lecture seule et ne peut pas être modifiée une fois qu'elle a été initialisée. Vous pouvez toutefois appliquer des opérations telles quesorted()
etreversed()
, qui renvoient une nouvelle liste sans modifier celle d'origine. - Une
MutableList
peut être modifiée après sa création, par exemple en ajoutant, en supprimant ou en modifiant des éléments. - Vous pouvez ajouter une liste d'éléments à une liste modifiable à l'aide de
addAll()
. - Utilisez une boucle
while
pour exécuter un bloc de code jusqu'à ce que l'expression prenne la valeur "false" et que vous quittiez la boucle.
while (expression) {
// While the expression is true, execute this code block
}
- Utilisez une boucle
for
pour itérer tous les éléments d'une liste :
for (item in myList) {
// Execute this code block for each element of the list
}
- Le modificateur
vararg
vous permet de transmettre un nombre variable d'arguments à une fonction ou un constructeur.
9. En savoir plus
- Présentation des collections Kotlin
- Liste
- Opérations spécifiques à List
- Liste : apprendre à utiliser Kotlin à l'aide d'un exemple
- Boucle : apprendre à utiliser Kotlin à l'aide d'un exemple
listOf()
mutableListOf()
- Boucles
while
- Boucles
for
- Nombre variable d'arguments
vararg
- Mot clé
this
- Affectations augmentées (par exemple, +=)