📘

Kotlin

Un langage issu de Java, open source, interopérable.

On estime qu’au fil des annĂ©es, Java va se perdre au profit de Kotlin. En sachant qu’un projet Kotlin peut contenir des fichiers Ă  la fois de Java et de Kotlin, la migration est en cours.

En 2018, Google le considĂšre comme une langage officiel, ce qui enclenche sa popularisation.

â„č
Langage similaire : Dart (Flutter) https://dart.dev/

Utilisations : mobile cross-platform, code natif pour Mac et Windows, Data Science, développement back end (Java EE), JavaScript, Android

📌
Pour aller plus loin : https://kotlinlang.org/ et mĂȘme un playground

Les fondamentaux


📌
Pour aller plus loin : https://kotlinlang.org/

Main

A l’image de Java, toute opĂ©ration doit ĂȘtre contenue dans la fonction Main pour ĂȘtre exĂ©cutĂ©e.

var phrase = "hello world !"
fun main(args: Array<String>) {
	println("Plus besoin du System.out.println, mais surtout : $phrase")
}

On remarque :

Types

En Kotlin, “tout est objet” : les types commencent donc tous par une majuscule.

Types de variables : Byte, Short, Int, Long, Float, Double, Char, Boolean, String, Array (pas de tableau avec les crochets).

⚠
Il existe également le type Any.
â„č
Il est Ă©galement possible d’utiliser tous les types Java (interopĂ©rabilitĂ©).

A noter : toutes les Strings sont itérables.

Variables

Une variable peut ĂȘtre dĂ©clarĂ©e de diffĂ©rentes façons :

Afin de diffĂ©rer l’affectation d’une variable, on utilise la propriĂ©tĂ© dĂ©lĂ©guĂ©e by avec lazy : l’affectation ne sera faite qu’au moment oĂč la lecture de cette variable est demandĂ©e. ConcrĂštement, le bout de code (appelĂ© lambda) sera exĂ©cutĂ© et la valeur rĂ©sultante sera utilisĂ©e.

fun main(args: Array<String>) {
	var mot = "nope."
	val info: String by lazy { "Le mot magique est $mot." }
  mot = "merciiiii"
	println(info) //Le mot magique est merciiiii.
}

Par dĂ©faut, un objet ne peut pas ĂȘtre nul : null safety.

Il est possible de rendre un objet nullable en le prĂ©cisant au moment de la dĂ©claration avec Type?. Dans ce cas, il faudra tester la nullitĂ© Ă  chaque utilisation de la variable avec l’opĂ©rateur ?..

var prenom: String?
var nbCaracteres: Int? = prenom?.length
//on obtient la longueur de la variable prenom 
//  ou "null" si la variable prenom est nulle

A noter :

Structures de contrĂŽle

Exemples issus de la documentation Kotlin.
fun maxOf(a: Int, b: Int): Int {
    if (a > b) {
        return a
    } else {
        return b
    }
}

// equivaut a l'expression
fun maxOf(a: Int, b: Int) = if (a > b) a else b
fun describe(obj: Any): String =
    when (obj) {
        1          -> "One"
        "Hello"    -> "Greeting"
        is Long    -> "Long" //check du type
        !is String -> "Not a string"
        else       -> "Unknown"
    }

// avec in
when {
    "orange" in items -> println("juicy")
    "apple" in items -> println("apple is fine too")
}

A noter : super switch (il break dùs qu’il trouve)

val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
    println(item)
}

// possible d'appeler l'indice
for (index in items.indices) {
    println("item at $index is ${items[index]}")
}

// possible d'appeler cle/valeur
for ((key,value) in items.withIndex()) {
	println("index at $index is $value")
}
val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
}

â„č
Les fonctions arrayOf() et mutableListOf() créent toutes les deux un tableau, en appelant ArrayList en Java.

Conversions de type

La conversion est implicite et comme les types sont des objets, on peut utiliser des fonctions de conversion (ex : unEntier.toLong()).

Les fonctions


📌
Pour aller plus loin : https://kotlinlang.org/docs/functions.html

Une fonction est définie avec fun.

On spécifie les paramÚtres avec leur nom, leur type et éventuellement une valeur par défaut : (x:Int=0).

On spĂ©cifie le type de la valeur retournĂ©e aprĂšs les deux-points : String, qui peut ĂȘtre vide Unit (void en Java).

fun afficherBonjour() : Unit {
	println("Bien le bonjour")
}

fun additionner(x:Int=0, y:Int=0) : Int {
	return x+y
}

Single-expression functions :

Si la fonction ne retourne qu’une expression simple, il est possible de raccourcir l’écriture avec un = qui remplace les accolades :

fun afficherBonjour() = println("Bien le bonjour")

fun additionner(x:Int=0, y:Int=0) = return x+y

vararg :

Il est possible de passer n paramĂštres Ă  une variable vararg pour un mĂȘme type dĂ©fini ou mĂȘme gĂ©nĂ©rique.

fun multiplier(vararg nombre: Float): Float {
    var res = 1.0f
    for (item in nombre) {
        res *= item
    }
    return res
}
println(multiplier(4.0f, 2.0f, 3.0f)) //24.0
println(multiplier(6.7f, 2.5f)) //16.75

Fonctions génériques :

Une fonction peut prendre un paramÚtre générique indiqué avec la notation diamant : fun <T>.

fun <T> creerUneListe(vararg valeurs:T): List<T> {
	var liste = ArrayList<T>()
	for(item in valeurs) {
		liste.add(item)
	}
	return liste
}
println(creerUneListe("vinaigre", "beurre", "chaussettes")) //[vinaigre, beurre, chaussettes]
println(creerUneListe(18, 96.3, "blop")) //[18, 96.3, blop]

Extensions

On peut étendre une classe ou interface avec une nouvelle fonctionnalité sans avoir à en hériter, grùce aux extensions : fun Class.fonctionDExtension(params) { 
 }.

Par exemple, pour ajouter une fonction d’extension à la classe Date pour afficher la date et l’heure :

import java.util.Date;

fun Date.afficherHm() =
	println("${this.hours}h${this.minutes}")

fun main() {
	Date().afficherHm()
}

A noter : this rĂ©fĂšre Ă  l’instance

📌
Pour aller plus loin : https://kotlinlang.org/docs/extensions.html

Opérateur Elvis

L’opĂ©rateur Elvis ?: est utilisĂ© pour retourner une valeur mĂȘme si l’expression est nulle.

var unMot: String? = null
val tailleMot = unMot?.length?:-1
println("La longueur du mot est $tailleMot")
//La longueur du mot est -1

Collections

Une collection est un groupe d’élĂ©ments de nombre variable.

Les principales collections de Kotlin sont List, Set et Map.

📌
Pour aller plus loin : https://developer.android.com/codelabs/basic-android-kotlin-collections?hl=fr#0 https://kotlinlang.org/docs/collections-overview.html

Lambdas

GrossiÚrement, une expression lambda est une fonction non déclarée, passée directement en paramÚtre à une fonction entre accolades : { expression }.

val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
    .filter { it.startsWith("a") }
    .sortedBy { it }
    .map { it.uppercase() }
    .forEach { println(it) }

A noter : it fait rĂ©fĂ©rence Ă  l’élĂ©ment en train d’ĂȘtre testĂ©

Une fonction anonyme (sans nom) est une alternative qui permet de spĂ©cifier le type de retour, elle peut Ă©galement ĂȘtre placĂ©e en paramĂštre d’une fonction : fun(param):Type = expression.

exemple.filter(fun(item) = item > 0)
📌
Pour aller plus loin : https://kotlinlang.org/docs/functions.html

Try / Catch

Classique.
var aTester: Int = try {
	// expression
} catch (e: Exception) {
	throw Exception(e)
}

Les classes


📌
Pour aller plus loin : https://kotlinlang.org/docs/classes.html

Une classe est dĂ©finie avec class et Ă©ventuellement d’autres mots-clĂ©s.

Une simple class aura un constructeur, les getter/setter pour chaque attribut, sauf un attribut val qui n’est pas modifiable donc n’aura pas de setter.

Une data class aura, en plus, les méthodes toString(), equals(), hashcode() et copy().

data class Personne(
		val id: Int,
		val nom: String,
		val prenom: String,
		val dateDeNaissance: Date? = null
)

Une classe abstraite est déclarée avec abstract class.

Une instance est crée sans le new de Java, simplement en appelant la classe et en renseignant les paramÚtres éventuels : var bob = Personne(1, "Dylan", "Bob", 24/05/1941).

Héritage

Par dĂ©faut, toutes les classes sont final et donc fermĂ©es Ă  l’hĂ©ritage.

On rend une classe ouverte Ă  l’hĂ©ritage avec open : open data class MaClasseMere(...).

Pour qu’une mĂ©thode de cette classe soit ouverte Ă  la surcharge (override), elle doit ĂȘtre open Ă©galement.

Une classe qui hĂ©rite d’une autre est dĂ©clarĂ©e avec les deux-points : (extends) : class MaClasseFille (...) : MaClasseMere. Les mĂ©thodes surchargĂ©es sont dĂ©clarĂ©es avec override.

open class UneClasseMere {
	open fun uneMethodeSurchargeable...
	fun uneMethodeNonSurchargeable...
}

class UneClasseFille : UneClasseMere {
	override fun uneMethodeSurchargeable...
	fun uneMethodeDeFille....
}

Champ et propriété

Les getter/setter peuvent ĂȘtre dĂ©finis manuellement.

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // parses the string and assigns values to other properties
    }

Interface

“Contrat”.

Une interface est déclarée avec interface.

Une classe qui implémente une interface est également déclarée avec les deux-points : (implements). Les méthodes surchargées sont déclarées avec override.

interface UneInterface {
	val UneConstante
	fun uneMethode
	fun uneAutreMethode
}

class UneClasse : UneInterface {
	override val UneConstante = "une valeur"
	override fun uneMethode...
	override fun uneAutreMethode...
}
â„č
Les mĂ©thodes dĂ©crites dans une interface sont forcĂ©ment surchargeables donc il n’est pas nĂ©cessaire de prĂ©ciser open.

Classe Enum

Liste de constantes de mĂȘme type.

Une énumération est déclarée avec enum class.

Chaque constante est un objet qui peut ĂȘtre initialisĂ©.

enum class JourOuvre {
	LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI
}

Classe Objet

Un singleton peut ĂȘtre créé avec une classe objet (ou companion object) dĂ©clarĂ©e avec object (tout ce qu’il y a dedans est static).

object DAOFactory {
	fun getDao(type: DAOType) =
	when(type) {
            DAOType.MEMORY-> ArticleDAOMemoryImpl()
//            DAOType.INTERNET -> ArticleDAOMemoryImpl()
//            DAOType.DB -> ArticleDAOMemoryImpl()
		}
}