đź–˛ DĂ©couvrir Kotlin

đź–˛ DĂ©couvrir Kotlin

Une histoire de syntaxe

Kotlin est un langage développé par Jetbrains, Jetbrains développe des environnements de développement pour pratiquement tous les langages les plus utilisés.

Kotlin en lui même est un langage fonctionnel et orienté objet. Là où java est 100% orienté objet, Kotlin permet plus de liberté d'utilisation et est ce qu'on appelle un langage moderne.

Il est bien moins verbeux que Java. Permet de faire moins d'erreur, produit des programmes plus stables, et est plus flexible.

Un exemple pour créer un POJO (ceux qui connaissent en Java sinon renseignez-vous c'est un principe de base) en Kotlin :

data class Customer(val name: String, val email: String, val company: String)

Et voila, cette ligne de code vous permet de créer une classe avec tout les getters, setters, equals(), hashCode(), toString() et copy().

Question 1 👉 Définissez la différence entre objet, orienté objet et fonctionnel ? Comment qualifie-t-on ces concepts ? Qu'est ce qu'un POJO ?

Fonctionnement

Kotlin permet de générer du bytecode pour JVM.

Un mot sur la JVM

La Java Virtual Machine est une belle invention qui date de 1995, et qui permet de faire un développement multi-plateforme. C'est un langage qui est compilé et interprété.

Question 2 👉 Définissez la différence entre un langage interprété et un langage compilé ? Citez quelques exemples de langage.

Lorsque vous codez en Java, votre code est compilé en byte code ce n'est pas un état directement interprétable par votre ordinateur, mais interprétable par une JVM. On appelle ça le build time.

Ce byte code est ensuite interprété lorsque vous lancez votre application sur l'appareil cible, on appelle ça le runtime. D'où le besoin d'avoir un JRE (Java Runtime Environment) pour executer un programme Java sur votre machine.

Mais le langage permet de compiler vers d'autres byte code ou binary code :

  • Javascript
  • C/C++
  • Swift
  • ObjectiveC
  • Any native executable

Hello world

Comment coder en Kotlin pour apprendre ?

Vous avez l'embarras du choix:

Pour des raisons de simplicité de configuration, je vous conseil d'utiliser le Playground Kotlin en ligne.

Le Hello world


Dans le playground vous devriez avoir déjà une fonction main et le Hello world

//Kotlin
fun main() {
    println("Hello world")
}

Voici le code Ă©quivalent en Java

 //Java
 public class HelloWorld {
     public static void main(String[] args) {
         System.out.println("Hello world!");
     }
 }
📝 Note Dans les deux cas il vous faut un point d'entrée main à votre programme. En Kotlin c'est une simple fonction déclarée avec le mot clé fun en java il vous faut une classe et à l'intérieur une méthode main

Kotlin et le typage

Kotlin est un langage fortement typé.

Question 3 👉 Définissez les points de différence entre un langage fortement typé et faiblement, voire, non typé ? Citez quelques exemples de langage.

Pour déclarer une variable en Kotlin

//On précise le type ici
val myHelloWorldStringVariable:String = "Hello there"

//Version alternative : 
//L'inférence de type permet d'éviter d'expliciter le type
val myHelloWorldStringVariable = "Hello there"
📝 Note On peut déclarer une variable en dehors de toute fonction ou classe, ce qui n'est pas possible en Java

Voici le code Ă©quivalent en Java

 public class HelloWorld {
     String myHelloWorldStringVariable = new String("Hello there");
 
     public static void main(String[] args) {
         System.out.println("Hello world!");
     }
 }

Les variables

Pour déclarer une variable on peut utiliser 2 mots clés : val et var

Question 4 👉 Chercher la différence entre les deux mots clés et expliquer avec vos mots.

Optional & Null safety 🌮

Attention on arrive à un endroit légèrement difficile. Du moins c'est pas commun sur les langages que vous avez vu jusqu'à aujourd'hui.

En kotlin on ne peut pas avoir une valeur null dans une variable. Contrairement au Java. En Java on peut faire ca :

public class HelloWorld
{
  // arguments are passed using the text field below this editor
  public static void main(String[] args)
  {
    OtherClass otherClass = null;
    //Ici j'obtiens le fameux "Null pointer exception" lors du runtime
    System.out.print(otherClass.myVar); 
  }
}

public class OtherClass {
 	String myVar = new String("Hello");
}

🚫 Si je compile ce code en Java, il va compiler (Buildtime) sans problème. Mais lorsque que je vais lancer mon programme (Runtime) il va planter.
Ce qui est pas top, car je ne vois le problème qu'au moment ou l'utilisateur va lancer le programme.


Essayons de faire la mĂŞme chose en Kotlin

Ajoutez dans votre fichier (main.kt) de test le code suivant :

fun main() {
    val otherClassInstance = OtherClass()
    println(otherClassInstance.myVar)
}

class OtherClass(var myVar:String = null)
Question 5 👉 Que se passe-t'il lorsque vous compilez ?

đźš« C'est impossible. En Kotlin si on indique pas explicitement qu'une variable peut contenir une valeur null alors il est impossible de build.

Comment on indique cela alors ?

Si je voulais que le build time passe sur mon code Kotlin, il faudrait que je modifie en faisant cela :

fun main() {
    val otherClassInstance = OtherClass()
    println(otherClassInstance.myVar)
}

//On a rajouté un signe "?" derrière notre type.
//Cela indique au compilateur que cette variable est parfois "null"
class OtherClass(var myVar:String? = null)

Non seulement cela compile, mais au runtime cela ne plante pas, mĂŞme si votre variable est null.

Comment gérer ce comportement ?

Souvent dans votre code vous allez devoir indiquer au programme que vous n'êtes pas certain qu'il y aura une valeur non null dans une variable à un instant T (Ex: je récupère une donnée du réseau)

Dans ce genre de cas, vous allez devoir unwrap 🌮 la variable, c'est en gros "Découvrir" ce qu'il y a dedans, et suivant ce que vous trouverez, exécutez tel ou tel code.

Si je prend l'exemple du réseau, imaginez le code suivant

//Je vais stocker la réponse hypothétique du réseau dans ma variable
var myNetworkResponse:String? = null 

//Ici j'appelle le réseau, et il renvoi une valeur (ou PAS)
myNetworkResponse = networkCall()

//Je veux récuperer la réponse du réseau si il y en a unne
myNetworkResponse?.let {  
    //La fonction `let` permet de unwrap 🌮 votre variable
    //Elle produit une valeur non optionnel "it" par defaut
    saveNetworkResponseToDataBase(it) 
    //"it" est certain d'exister, et est non null
}
Question 6 👉 Selon vous quelle est la signature de la fonction networkCall ? Ecrivez la et faites valider par l'enseignant.
Note đź“ť Cette fonction n'existe pas, on vous demande Ă  partir du code ci-dessus d'imaginer sa signature

Note 📝 Le même exemple mais avec le nommage explicite de la donnée unwrap 🌮
//On donne un nom explicite "responseData" au lieu de "it"
myNetworkResponse?.let { responseData ->
    //La fonction `let` permet de unwrap 🌮 votre variable
    saveNetworkResponseToDataBase(responseData) 
}

On a vu la fonction let mais il en existe d'autres pour gérer les optionnels et le null safety

Pour en apprendre plus voici un lien vers la documentation Kotlin qui explique ce comportement et comment l'utiliser au mieux.

Question 7 👉 Comment pourriez vous faire pour exécuter du code dans le cas ou la valeur de l'unwrapping 🌮 est effectivement null, pour déclencher un nouvel appel réseau par exemple ? Trouvez l'opérateur adéquat et inscrivez le code d'exemple complété dans un fichier.

Attention  ⚠️

A l'inverse vous pouvez forcer le compilateur Ă  ignorer qu'une variable est potentiellement null

Grâce au symbole !! : il est à éviter le plus possible car il force le compilateur à ne pas vérifier si cette variable est vide avant de l’utiliser.

Cela peut être pratique pour éviter l’unwrapping 🌮 et donc quelques lignes de code, mais c’est aussi très dangereux car le comportement est imprévisible.

//On bourrine. Mais si le network plante, le programme plante.
saveNetworkResponseToDataBase(myNetworkResponse!!)

Les fonctions

En Kotlin vous pouvez déclarer des fonctions n'importe où dans votre programme. Une fonction peut aussi être dans une variable, comme en javascript.


fun myFunction()

fun myFunction(): String {
    return "Hello World"
}

//Type inference, Kotlin devine que la fonction sera de type string.
//On utilise également la syntaxe simplifiée pour une fonction ici
fun myFunction() = "Hello world" 

//Avec un parametre, les parametres doivent-etre typé
fun numberOfCharactersInString(inputString: String): Int {
    return parameter.length
}

fun greetUser(name: String, lastname:String, city:String) {
    println("Hello $name $lastname, you live in $city")
}

//Lambda dans une variable
var myVariable = {
        println("Hello")
    }

//On peut aussi passer des parametres
var myVariable:(name: String) -> Unit = {name -> 
    println("Hello $name") 
}


//Fonction anonyme ex 1
val square = fun(x: Int) = x * x

//Fonction anonyme ex 2
val greetings = fun(name: String) = "Hello $name"  

Puisque Kotlin est aussi un langage fonctionnel, il est important de bien comprendre comment et quand utiliser des fonctions. Nous verrons tout au long de l'introduction différents moyens d'utiliser les fonctions et variables.

Note 📝: La syntaxe pour concatener une string est + ou à l'intérieur des double quote vous pouvez utiliser "Je concatene avec $maVariable". Et pour accéder a une propriété de votre variable : "Je concatene avec ${maVariable.maProp}"

Appeler une fonction

Il existe plusieurs façon d'appeler une fonction en Kotlin, notamment au niveau des paramètres.

Dans le langage les paramètres sont nommés, même si on est pas obligé de préciser le nom du paramètre c'est très utile pour s'y retrouver.
numberOfCharactersInString("Hello"); //5
//J'appelle ma fonction en précisant le nom des parametres
numberOfCharactersInString(inputString = "Hello")

Control flow (if, for, when)

Je ne vais pas paraphraser la documentation qui fait très bien son boulot ici.

Question 8 🤌 Quel est l'équivalent du switch en Kotlin ?
Question 9 👉 Créez un tableau de string et affichez chaque valeur dans la sortie console. Faites valider par l'enseignant.

Les classes

Kotlin est aussi un langage objet, on déclare une classe qui possède une propriété comme ceci :

//Le constructeur est dans la signature de la classe entre ()
class MyClass(val myProperty:String?)

//Pour instancier cette classe
val myClassInstance = MyClass("hello")

Ou

class MyClass {
    var myVar:String? = null
    
    constructor(myVarValue: String?){
        myVar = myVarValue
    }
}

Sans propriété :

class MyClass

Voici le code Ă©quivalent en Java

class MyClass {
    
    String myProperty;
    
    public MyClass(String myProperty){
        this.myProperty = myProperty;
    }
}

Il est très rapide et concis de créer une classe en Kotlin. Et puisque c'est un langage assez malin il y a pas mal de raccourcis sous forme de mot clés.

Les classes de données

Un concept qui n'existe pas partout, les data class sont en fait un type de classe dédié au stockage de la donnée.

La différence avec les classes normales est que le compilateur va vous créer automatiquement des fonctions utilitaires sur vos data class.

Les fonctions automatiquement générées :

  • equals()
  • toString()
  • hashCode()
  • copy()
  • ...
Retrouvez la documentation sur les data class

Exemple de data class

data class User(val name: String, val age: Int, val gender: Gender)

val myUser = User(name = "Roger", age = 21, gender = Gender.MALE)

//To string est généré automatiquement donc tout va bien
println(myUser)

Encore une fois on remarque qu'un mot clé du langage va nous permettre de gagner du temps sur le développement tout en gardant des concepts standards.

Remarquez le type Gender, c'est un enum en Kotlin vous pouvez utiliser ce genre de variable pour définir un set de valeur prédéfini pour un objet.

Classes Enum

Très simple a comprendre mais redoutablement efficace pour construire un programme très robuste, les classes enum permettent de lister des valeurs possibles d'un type donné.

Exemple avec notre Gender

enum class Gender {
    FEMALE,
    MALE,
    OTHER
}

Cela permet d'éviter d'hardcoder des valeurs et d'éviter d'utiliser des constantes String qui sont fastidieuses à travailler et génératrice d'erreur.

data class User(val name: String, val age: Int, val gender: Gender)

val user1 = User(name = "Roger", age = 21, gender = Gender.FEMALE)

Ainsi toute variable ayant le type Gender ne pourra prendre qu'une des 3 valeurs de l'enum possible.

Question 10 👉 Trouvez 3 exemples d'utilisation de enum qui vous permettrait de simplifier un problème ?

Les mots clés

Companion, Object, Lateinit, let, with, run ne représentent qu'une  partie émergée de l'iceberg.

Kotlin est un langage avec beaucoup de mots-clés 🔑🔑🔑 , en voici une référence.

Pourquoi autant de mot clés ? Pour que vous écriviez le moins de code possible, la vision derrière Kotlin c'est la concision tout en conservant la clarté du code. Chaque mot clé est un travail redondant qui est optimisé par le compilateur et donc évite au développeur d'écrire du code. Plus on écrit de code, plus on a de chance de faire des erreurs.
Question 11 👉 Codez un programme faisant l'usage d'un singleton, en utilisant le mot-clé approprié.

Aller plus loin

Pour apprendre et maitriser un nouveau langage, il n'y a pas de solution simple.

Il faut vous entrainer Pour le dernier exercice, on va s'entrainer un peu.

Allez sur le site Kotlin Koans en cliquant sur le lien et réalisez les exercices à partir de la section "Conventions".

Kotlin Playground: Edit, Run, Share Kotlin Code Online
Dernière question 🤌 : Ecrivez une rapide explication des Generics, il est possible que vous deviez présenter le concept devant la promo. Prévenez l'enseignant quand vous avez terminé cette question.

Next Step : Gradle

Pour attaquer la suite et comprendre le mécanisme derrière la compilation d'une app Android nous devons regarder Gradle de plus près.

RĂ©alisez la fiche suivante : Introduction Ă  Gradle.

Commentaires

Connectez vous ou devenez un membre de Async pour rejoindre la conversation.
Entrez un mail ici pour recevoir un lien de connexion, super simple ⚡️