Après les petites astuces vues précédemment, voyons un peu en gros la syntaxe de Kotlin. C'est un mélange de Java, de Scala, de C# et certainement d'autres encore.
Il n'y a
plus besoin de point-virgule, et c'est tellement simple qu'on se demande pourquoi on en avait besoin...
Plus déroutant, il n'y a
plus de fonctions statiques. A la place, on les écrit où on veut mais hors d'une classe, mais quand même dans un package. Pour les appeler, on les importe individuellement ou en groupe (import ...*), puis on les appelle sans nom de classe, comme un bon vieux C.
Comme dans Scala, chaque déclaration de variable (de fonction ou de classe) doit être précédée de
val ou
var, et suivi du type et si nécessaire d'une valeur. Avec
val, la variable devient invariable :-). C'est le mot clé final de Java. On pourra constater qu'en pratique la plupart de nos variables sont en réalité des constantes. En plus, Kotlin permet de retarder le calcul de certaines constantes, ce qui permet de déclarer encore plus de constantes qu'en Scala.
Une variable dont le type est suivi d'un point interrogation peut contenir null, les autres ne peuvent jamais contenir null. Plusieurs opérateurs originaux permet de contrôler la nullité d'un variable afin de ne jamais cracher avec un NullPointerException.
Très pratique, comme en Scala, le type peut-être très omis pour être déduit par le compilateur. Non seulement le code est plus concis, plus lisible, mais il est aussi plus facile à maintenir lorsqu'on change un type.
Exemples :
val hello: String = "bonjour"
var x: String?
var y = hello
La
déclaration des fonctions ressemble à Scala en remplaçant le
def par
fun. Pour le corps, en revanche on peut choisir entre un style impératif comme java ou un style fonction comme Scala. Les deux ont leur avantage selon l'objectif et selon l'auteur, le choix est appréciable.
Comme plus haut, le type de retour peut-être souvent omis. Un type de retour correspond à un procédure qui ne retourne aucune valeur (void en Java). Enfin les paramètres accepter des valeurs par défaut.
Les fonctions peuvent aussi être imbriquées, c'est à dire définie à l'intérieur d'une autre fonction, afin de limiter leur visibilité, de les rapprocher de leur lieu d'usage, et de pouvoir utiliser les paramètres de la fonction englobante sans devoir les passer explicitement en paramètre.
Exemples :
fun square(x: Double): Double {
return x * x
}
fun square(x: Double) = { x * x}
fun square(x: Double) = x * x
fun displaySquare(x: Double = 2.0): Unit = println (square(x))
Comme dans Scala et Java 8, les paramètres et variables peuvent
référencer des fonctions. Pour faire simple, leur type s'écrit
(paramètres) -> Retour. Juste les parenthèses vides désigne une fonction sans paramètre, et un retour Unit une procédure qui ne retourne rien.
Exemples :
val f: (String, Int) -> String
val g: (String) -> Unit
val h: () -> Int
On peut aussi
étendre un type, c'est à dire ajouter une fonction dans une classe mais sans modifier celle-ci. On peut donc ajouter une fonction à la classe String. C'est principalement un artifice syntaxique pour avoir un code plus consistant, puisque les fonctions ajoutées sont appelées avec la même notation que les fonctions réellement définie dans la classe.
Exemple :
fun String.wordCount() = this.split(' ').size
val wc = "coucou la compagnie".wordCount()
Les structures de contrôles sont classiques à l'exception de la boucle
for et du branchement multi-directionnel
when. La boucle peut être appelé sur une liste ou un segment. Le when accepte quasiment n'importe quelle condition.
Exemple :
for (i in 0..5) println(i)
for (s in "coucou la compagnie".split(' ')) println (s)
val b = when (a) {
is Int -> "un entier"
"trésor" -> "trouvé"
else -> "par défaut"
}
Evidemment, c'est très loin d'être exhaustif, le but de ce post était de donner un aperçu des syntaxes pour mieux suivre les prochains articles.