Contactez-nous

Inférence de type : laisser Kotlin deviner

Découvrez comment l'inférence de type en Kotlin simplifie la déclaration de variables en laissant le compilateur deviner le type. Moins de code, même sécurité.

Introduction : qu'est-ce que l'inférence de type ?

L'une des caractéristiques qui contribuent grandement à la concision du code Kotlin est son puissant système d'inférence de type. En termes simples, l'inférence de type est la capacité du compilateur Kotlin à déterminer automatiquement le type d'une variable sans que vous ayez besoin de le déclarer explicitement dans le code.

Vous avez peut-être déjà remarqué dans les exemples précédents que nous n'avions pas toujours écrit le type après le nom de la variable (par exemple, `: Int` ou `: String`). Pourtant, le code fonctionnait parfaitement. C'est l'inférence de type en action ! Le compilateur "devine" ou "infère" le type approprié en se basant sur la valeur qui est assignée à la variable lors de sa déclaration.

Il est important de souligner que même avec l'inférence de type, Kotlin reste un langage à typage statique. Le type de chaque variable est fixé au moment de la compilation et ne change pas. L'inférence de type est simplement un confort syntaxique qui allège le code sans sacrifier la sécurité et les vérifications offertes par le typage statique.

Comment Kotlin déduit-il le type ?

Le mécanisme est assez intuitif. Lorsque vous déclarez une variable (`val` ou `var`) et que vous lui assignez immédiatement une valeur (ce qu'on appelle l'initialisation), le compilateur examine la valeur à droite du signe `=`. En fonction de la nature de cette valeur littérale ou du type retourné par l'expression, il détermine le type le plus approprié pour la variable.

Voici comment cela fonctionne pour les types de base que nous avons vus :

  • Si vous assignez un nombre entier (sans point décimal), Kotlin infère le type `Int` par défaut.
  • Si vous assignez un nombre avec un point décimal, Kotlin infère le type `Double` par défaut.
  • Si vous assignez `true` ou `false`, Kotlin infère le type `Boolean`.
  • Si vous assignez une valeur entre guillemets doubles `""`, Kotlin infère le type `String`.
fun main() {
    // Le compilateur infère les types ici

    val message = "Apprendre Kotlin" // Inférence: String
    val year = 2024               // Inférence: Int
    val piValue = 3.14159         // Inférence: Double
    val isLearning = true         // Inférence: Boolean

    // Vous pouvez vérifier le type si nécessaire (pour la démonstration)
    println(message::class.simpleName) // Affiche: String
    println(year::class.simpleName)    // Affiche: Int
    println(piValue::class.simpleName) // Affiche: Double
    println(isLearning::class.simpleName)// Affiche: Boolean

    // Même chose avec var
    var counter = 0               // Inférence: Int
    counter = 10                  // OK, toujours un Int
    // counter = "hello"          // Erreur: Type mismatch. Inferred type is Int but String was expected
}

Comme le montre la dernière ligne commentée, une fois que le type a été inféré (ici `Int` pour `counter`), la variable ne peut accepter que des valeurs de ce type. La sécurité du typage statique est préservée.

Quand l'inférence de type ne fonctionne pas ou est déconseillée

L'inférence de type est très pratique, mais elle a ses limites et il y a des situations où vous devrez (ou devriez) spécifier le type explicitement :

  • Variables non initialisées immédiatement : Si vous déclarez une variable sans lui donner de valeur initiale (ce qui est possible pour les `var` sous certaines conditions, ou pour des propriétés de classe), le compilateur ne peut pas deviner le type. Vous devez le spécifier.
  •     // var name // Erreur: Property must be initialized or be abstract
        var name: String // OK: Type spécifié, mais doit être initialisé avant utilisation
        // println(name) // Erreur: Variable 'name' must be initialized
        name = "CertiQuizz"
        println(name)
    
  • Paramètres de fonction et types de retour : Les types des paramètres d'une fonction doivent toujours être déclarés explicitement. Le type de retour d'une fonction doit généralement aussi être déclaré, sauf pour les fonctions dites "single-expression" où il peut être inféré.
  •     // fun add(a, b) = a + b // Erreur: les types de paramètres sont requis
        fun add(a: Int, b: Int): Int { // Types explicites pour params et retour
            return a + b
        }
    
        // Inférence possible pour le retour d'une fonction single-expression
        fun multiply(a: Int, b: Int) = a * b // Retour Int inféré
    
  • Lorsque le type souhaité est un supertype : Si vous assignez une valeur d'un certain type, mais que vous voulez que la variable soit d'un type parent (supertype), vous devez le déclarer explicitement.
  •     val number = 100 // Inféré comme Int
        val anyNumber: Number = 100 // Explicitement déclaré comme Number (supertype de Int)
    
  • Lorsque le type inféré par défaut n'est pas celui souhaité : Si vous voulez un `Long` au lieu d'un `Int`, ou un `Float` au lieu d'un `Double`, vous pouvez soit ajouter un suffixe au littéral (`100L` pour Long, `3.14f` pour Float), soit déclarer le type explicitement.
  •     val longNumber = 100L // Inféré comme Long grâce au suffixe L
        val smallPi: Float = 3.14159 // Explicitement Float (le littéral est Double par défaut)
        val anotherLong: Long = 100 // Explicitement Long
    

Les bénéfices de l'inférence de type

Le principal avantage de l'inférence de type est la concision. Elle réduit la quantité de code répétitif à écrire, rendant le code source plus court et souvent plus facile à lire, car il se concentre sur les noms des variables et leurs valeurs plutôt que sur la déclaration explicite des types (qui est souvent évidente d'après la valeur assignée).

Cela améliore la productivité du développeur sans compromettre la sécurité. Les vérifications de type sont toujours effectuées par le compilateur, garantissant que vous n'effectuez pas d'opérations invalides sur vos variables.

En résumé, l'inférence de type est un outil puissant qui rend le code Kotlin plus agréable à écrire et à lire, tout en maintenant la robustesse du typage statique. Il est bon de l'utiliser là où c'est naturel, mais il ne faut pas hésiter à spécifier explicitement les types lorsque cela améliore la clarté ou est requis par le compilateur.