
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.
Utilisations : mobile cross-platform, code natif pour Mac et Windows, Data Science, développement back end (Java EE), JavaScript, Android
Les fondamentaux
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 :
- une fonction est déclarée avec
fun
- les points virgules ne sont pas nĂ©cessaires (voire mĂȘme obsolĂštes)
- les paramĂštres dâune fonction sont dĂ©clarĂ©s avec
args: type
- une variable est déclarée avec
var
- il est possible de dĂ©clarer une variable en dehors dâune fonction ou dâune classe
- Kotlin fait de lâinfĂ©rence de type : le type est dĂ©terminĂ© dynamiquement
- Kotlin fait de la String interpolation : il est possible de mettre une variable dans une chaine de caractĂšres
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).
Any. A noter : toutes les Strings sont itérables.
Variables
Une variable peut ĂȘtre dĂ©clarĂ©e de diffĂ©rentes façons :
varpour une variable dont la valeur est modifiable
valpour une variable dont la valeur est attribuĂ©e au moment du runtime et qui ne peut plus ĂȘtre modifiĂ©e ensuite
const varpour une variable dont la valeur est attribuĂ©e Ă la compilation et qui ne peut plus ĂȘtre modifiĂ©e ensuite
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 nulleA noter :
- il nây a pas dâopĂ©rateur ternaire mais certains Ă©quivalents (vus plus loin)
- un intervalle (range) peut ĂȘtre dĂ©fini avec
..(oudownToen décroissant)
Structures de contrĂŽle
Exemples issus de la documentation Kotlin.
- if
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- when
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)
- for
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")
}- while (et do while)
val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
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
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
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.
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)
Try / Catch
Classique.
var aTester: Int = try {
// expression
} catch (e: Exception) {
throw Exception(e)
}
Les classes
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...
}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()
}
}