Partager cette page

Apprendre X en Y minutes

Où X=Kotlin

Kotlin est un langage de programmation à typage statique pour la JVM, Android et le navigateur. Il est 100% interopérable avec Java. Pour en savoir plus, cliquez ici

// Les commentaires d'une seule ligne commencent par //
/*
Les commentaires de plusieurs lignes ressemblent à ceci.
*/

// Le mot-clé "package" fonctionne de la même manière qu'en Java.
   package com.learnxinyminutes.kotlin

/*
Le point d'entrée d'un programme Kotlin est une fonction appelée "main".
La fonction reçoit un tableau contenant tous les arguments de la ligne de commande.
Depuis Kotlin 1.3, la fonction "main" peut également être définie sans
tout paramètre.
*/
fun main(args: Array<String>) {
    /*
    La déclaration des valeurs se fait en utilisant soit "var" soit "val".
     Les déclarations "val" ne peuvent pas être réaffectées, alors que les déclarations "vars" le peuvent.
    */
    val fooVal = 10 // nous ne pouvons pas plus tard réaffecter fooVal à autre chose
    var fooVar = 10
    fooVar = 20 // fooVar peut être réaffecté

    /*
    Dans la plupart des cas, Kotlin peut déterminer quel est le type de variable,
    afin de ne pas avoir à le préciser explicitement à chaque fois.
    Nous pouvons déclarer explicitement le type d'une variable de cette manière :
    */
    val foo: Int = 7

    /*
    Les chaînes de caractères peuvent être représentées de la même manière qu'en Java.
    L'échappement se fait avec une barre oblique inversée.
    */
    val fooString = "Ma chaine est là !"
    val barString = "Imprimer sur une nouvelle ligne ? \nPas de problème !"
    val bazString = "Vous voulez ajouter une tabulation ? \tPas de problème !"
    println(fooString)
    println(barString)
    println(bazString)

    /*
    Une chaîne brute est délimitée par une triple citation (""").
    Les chaînes de caractères brutes peuvent contenir des nouvelles lignes et tout autre caractère.
    */
    val fooRawString = """
fun helloWorld(val name : String) {
   println("Bonjour, le monde !")
}
"""
    println(fooRawString)

    /*
    Les chaînes de caractères peuvent contenir des expressions modèles.
    Une expression modèle commence par le signe du dollar ($).
    */
    val fooTemplateString = "$fooString as ${fooString.length} caractères"
    println(fooTemplateString) // => Ma chaine est là ! comporte 18 caractères

    /*
    Pour qu'une variable soit considérée comme nulle, elle doit être explicitement spécifiée comme nulable.
    Une variable peut être spécifiée comme nulle en ajoutant un ? à son type.
    On peut accéder à une variable nulable en utilisant l'opérateur ?
    Nous pouvons utiliser l'opérateur ?: pour spécifier une valeur alternative à utiliser
    si une variable est nulle.
    */
    var fooNullable: String? = "abc"
    println(fooNullable?.length) // => 3
    println(fooNullable?.length ?: -1) // => 3
    fooNullable = null
    println(fooNullable?.length) // => null
    println(fooNullable?.length ?: -1) // => -1

    /*
    Les fonctions peuvent être déclarées en utilisant le mot-clé "fun".
    Les arguments des fonctions sont spécifiés entre parenthèses après le nom de la fonction.
    Les arguments de fonction peuvent éventuellement avoir une valeur par défaut.
    Le type de retour de la fonction, si nécessaire, est spécifié après les arguments.
    */
    fun hello(name: String = "world"): String {
        return "Bonjour, $name!"
    }
    println(hello("foo")) // => Bonjour, foo!
    println(hello(name = "bar")) // => Bonjour, bar!
    println(hello()) // => Bonjour, le monde!

    /*
    Un paramètre de fonction peut être marqué avec le mot-clé "vararg
    pour permettre de passer un nombre variable d'arguments à la fonction.
    */
    fun varargExample(vararg names: Int) {
        println("L'argument comporte ${names.size} éléments.")
    }
    varargExample() // => L'argument a 0 éléments
    varargExample(1) // => L'argument a 1 éléments
    varargExample(1, 2, 3) // => L'argument comporte 3 éléments

    /*
    Lorsqu'une fonction est constituée d'une seule expression, les parenthèses bouclées peuvent
    être omis. Le corps est spécifié après le symbole =.
    */
    fun odd(x: Int): Boolean = x % 2 == 1
    println(odd(6)) // => false
    println(odd(7)) // => true

    // Si le type de retour peut être déduit, alors nous n'avons pas besoin de le préciser.
    fun even(x: Int) = x % 2 == 0
    println(even(6)) // => true
    println(even(7)) // => false

    // Les fonctions peuvent prendre des fonctions d'arguments et des fonctions de retour.
    fun not(f: (Int) -> Boolean): (Int) -> Boolean {
        return {n -> !f.invoke(n)}
    }
    // Les fonctions nommées peuvent être spécifiées comme arguments en utilisant l'opérateur :: .
    val notOdd = not(::odd)
    val notEven = not(::even)
    // Les expressions lambda peuvent être spécifiées en tant qu'arguments.
    val notZero = not {n -> n == 0}
    /*
    Si un lambda n'a qu'un seul paramètre
    alors sa déclaration peut être omise (ainsi que le ->).
    Le nom du paramètre unique sera "it".
    */
    val notPositive = not {it > 0}
    for (i in 0..4) {
        println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")
    }

    // Le mot-clé "class" est utilisé pour déclarer les classes.
    class ExampleClass(val x: Int) {
        fun memberFunction(y: Int): Int {
            return x + y
        }

        infix fun infixMemberFunction(y: Int): Int {
            return x * y
        }
    }
    /*
    Pour créer une nouvelle instance, nous appelons le constructeur.
    Notez que Kotlin n'a pas de mot-clé "new" .
    */
    val fooExampleClass = ExampleClass(7)
    // Les fonctions des membres peuvent être appelées en utilisant la notation par points.
    println(fooExampleClass.memberFunction(4)) // => 11
    /*
    Si une fonction a été marquée avec le mot-clé "infix", elle peut être
    appelé en utilisant la notation infixe.
    */
    println(fooExampleClass infixMemberFunction 4) // => 28

    /*
    Les classes de données sont une façon concise de créer des classes qui ne contiennent que des données.
    Les méthodes "hashCode"/"equals" et "toString" sont générées automatiquement.
    */
    data class DataClassExample (val x: Int, val y: Int, val z: Int)
    val fooData = DataClassExample(1, 2, 4)
    println(fooData) // => DataClassExample(x=1, y=2, z=4)

    // Les classes de données ont une methode "copy".
    val fooCopy = fooData.copy(y = 100)
    println(fooCopy) // => DataClassExample(x=1, y=100, z=4)

    // Les objets peuvent être déstructurés en plusieurs variables.
    val (a, b, c) = fooCopy
    println("$a $b $c") // => 1 100 4

    // La déstructuration en boucle "for"
    for ((a, b, c) in listOf(fooData)) {
        println("$a $b $c") // => 1 100 4
    }

    val mapData = mapOf("a" to 1, "b" to 2)
    // Map.Entry est également déstructurable
    for ((key, value) in mapData) {
        println("$key -> $value")
    }

    // La fonction "with" est similaire à la déclaration "with" de JavaScript.
    data class MutableDataClassExample (var x: Int, var y: Int, var z: Int)
    val fooMutableData = MutableDataClassExample(7, 4, 9)
    with (fooMutableData) {
        x -= 2
        y += 2
        z--
    }
    println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8)

    /*
    Nous pouvons créer une liste en utilisant la fonction "listOf".
    La liste sera immuable - les éléments ne peuvent être ajoutés ou supprimés.
    */
    val fooList = listOf("a", "b", "c")
    println(fooList.size) // => 3
    println(fooList.first()) // => a
    println(fooList.last()) // => c
    // Les éléments d'une liste sont accessibles par leur index.
    println(fooList[1]) // => b

    // Une liste mutable peut être créée en utilisant la fonction "mutableListOf".
    val fooMutableList = mutableListOf("a", "b", "c")
    fooMutableList.add("d")
    println(fooMutableList.last()) // => d
    println(fooMutableList.size) // => 4

    // Nous pouvons créer un ensemble en utilisant la fonction "setOf".
    val fooSet = setOf("a", "b", "c")
    println(fooSet.contains("a")) // => true
    println(fooSet.contains("z")) // => false

    // Nous pouvons créer un map en utilisant la fonction "mapOf".
    val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9)
    // Map values can be accessed by their key.
    println(fooMap["a"]) // => 8

    /*
    Les séquences représentent des collections évaluées paresseusement.
    Nous pouvons créer une séquence en utilisant la fonction "generateSequence".
    */
    val fooSequence = generateSequence(1, { it + 1 })
    val x = fooSequence.take(10).toList()
    println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    // Un exemple d'utilisation d'une séquence pour générer des nombres de Fibonacci :
    fun fibonacciSequence(): Sequence<Long> {
        var a = 0L
        var b = 1L

        fun next(): Long {
            val result = a + b
            a = b
            b = result
            return a
        }

        return generateSequence(::next)
    }
    val y = fibonacciSequence().take(10).toList()
    println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    // Kotlin offre des fonctions d'ordre supérieur pour le travail avec les collections.
    val z = (1..9).map {it * 3}
                  .filter {it < 20}
                  .groupBy {it % 2 == 0}
                  .mapKeys {if (it.key) "even" else "odd"}
    println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]}

    // Une boucle "for" peut être utilisée avec tout ce qui fournit un itérateur.
    for (c in "hello") {
        println(c)
    }

    // Les boucles "while" fonctionnent de la même manière que les autres langues.
    var ctr = 0
    while (ctr < 5) {
        println(ctr)
        ctr++
    }
    do {
        println(ctr)
        ctr++
    } while (ctr < 10)

    /*
    "if" peut être utilisé comme une expression qui renvoie une valeur.
    Pour cette raison, l'opérateur ternaire ?: n'est pas nécessaire dans Kotlin.
    */
    val num = 5
    val message = if (num % 2 == 0) "even" else "odd"
    println("$num is $message") // => 5 is odd

    // Le terme "when" peut être utilisé comme alternative aux chaînes "if-else if".
    val i = 10
    when {
        i < 7 -> println("first block")
        fooString.startsWith("hello") -> println("second block")
        else -> println("else block")
    }

    // "when" peut être utilisé avec un argument.
    when (i) {
        0, 21 -> println("0 or 21")
        in 1..20 -> println("in the range 1 to 20")
        else -> println("none of the above")
    }

    // "when" peut être utilisé comme une fonction qui renvoie une valeur.
    var result = when (i) {
        0, 21 -> "0 or 21"
        in 1..20 -> "in the range 1 to 20"
        else -> "none of the above"
    }
    println(result)

    /*
    Nous pouvons vérifier si un objet est d'un type particulier en utilisant l'opérateur "is".
    Si un objet passe avec succès une vérification de type, il peut être utilisé comme ce type sans
    en le diffusant explicitement.
    */
    fun smartCastExample(x: Any) : Boolean {
        if (x is Boolean) {
            // x est automatiquement converti en booléen
            return x
        } else if (x is Int) {
            // x est automatiquement converti en Int
            return x > 0
        } else if (x is String) {
            // x est automatiquement converti en String
            return x.isNotEmpty()
        } else {
            return false
        }
    }
    println(smartCastExample("Bonjour, le monde !")) // => true
    println(smartCastExample("")) // => false
    println(smartCastExample(5)) // => true
    println(smartCastExample(0)) // => false
    println(smartCastExample(true)) // => true

    // Le Smartcast fonctionne également avec le bloc when
    fun smartCastWhenExample(x: Any) = when (x) {
        is Boolean -> x
        is Int -> x > 0
        is String -> x.isNotEmpty()
        else -> false
    }

    /*
    Les extensions sont un moyen d'ajouter de nouvelles fonctionnalités à une classe.
    C'est similaire aux méthodes d'extension C#.
    */
    fun String.remove(c: Char): String {
        return this.filter {it != c}
    }
    println("Hello, world!".remove('l')) // => Heo, word!
}

// Les classes Enum sont similaires aux types Java enum.
enum class EnumExample {
    A, B, C // Les constantes Enum sont séparées par des virgules.
}
fun printEnum() = println(EnumExample.A) // => A

// Puisque chaque enum est une instance de la classe enum, ils peuvent être initialisés comme :
enum class EnumExample(val value: Int) {
    A(value = 1),
    B(value = 2),
    C(value = 3)
}
fun printProperty() = println(EnumExample.A.value) // => 1

// Chaque énum a des propriétés pour obtenir son nom et son ordinal (position) dans la déclaration de classe de l'énum :
fun printName() = println(EnumExample.A.name) // => A
fun printPosition() = println(EnumExample.A.ordinal) // => 0

/*
Le mot-clé "objet" peut être utilisé pour créer des objets singleton.
On ne peut pas l'instancier mais on peut se référer à son instance unique par son nom.
Cela est similaire aux objets singleton de Scala.
*/
object ObjectExample {
    fun hello(): String {
        return "Bonjour"
    }

    override fun toString(): String {
        return "Bonjour, c'est moi, ${ObjectExample::class.simpleName}"
    }
}


fun useSingletonObject() {
    println(ObjectExample.hello()) // => hello
    // Dans Kotlin, "Any" est la racine de la hiérarchie des classes, tout comme "Object" l'est dans Java
    val someRef: Any = ObjectExample
    println(someRef) // => Bonjour, c'est moi, ObjectExample
}


/* L'opérateur d'assertion non nulle ( !!) convertit toute valeur en un type non nul et
   lance une exception si la valeur est nulle.
*/
var b: String? = "abc"
val l = b!!.length

data class Counter(var value: Int) {
    // surcharge Counter += Int
    operator fun plusAssign(increment: Int) {
        this.value += increment
    }

    // surcharge Counter++ et ++Counter
    operator fun inc() = Counter(value + 1)

    // surcharge Counter + Counter
    operator fun plus(other: Counter) = Counter(this.value + other.value)

    // surcharge Counter * Counter
    operator fun times(other: Counter) = Counter(this.value * other.value)

    // surcharge Counter * Int
    operator fun times(value: Int) = Counter(this.value * value)

    // surcharge Counter dans Counter
    operator fun contains(other: Counter) = other.value == this.value

    // surcharge Counter[Int] = Int
    operator fun set(index: Int, value: Int) {
        this.value = index + value
    }

    // surcharge Counter instance invocation
    operator fun invoke() = println("The value of the counter is $value")

}
/* Vous pouvez également surcharger les opérateurs par des méthodes d'extension */
// surcharge -Counter
operator fun Counter.unaryMinus() = Counter(-this.value)

fun operatorOverloadingDemo() {
    var counter1 = Counter(0)
    var counter2 = Counter(5)
    counter1 += 7
    println(counter1) // => Counter(value=7)
    println(counter1 + counter2) // => Counter(value=12)
    println(counter1 * counter2) // => Counter(value=35)
    println(counter2 * 2) // => Counter(value=10)
    println(counter1 in Counter(5)) // => false
    println(counter1 in Counter(7)) // => true
    counter1[26] = 10
    println(counter1) // => Counter(value=36)
    counter1() // => La valeur est 36
    println(-counter2) // => Counter(value=-5)
}

Lectures complémentaires


Vous avez une suggestion ? Peut-être une correction ? Ouvrez un ticket sur GitHub, ou faites vous-même une pull request !

Version originale par S Webber, mis à jour par 1 contributeur(s).