Kotlin a été initialement conçu comme une alternative sûre et concise à Java. L'un de ses points forts est son mécanisme développé d'inférence de type, qui permet d'écrire un code moins verbeux sans perte de typage. L'inférence de type a été inspirée par des langages fonctionnels (comme Scala et Haskell), ainsi que par des tendances modernes dans la conception de langages à typage statique.
Dans Java et d'autres langages statiques, il est nécessaire de spécifier explicitement les types, ce qui conduit à une redondance de code. Cependant, l'absence de types explicites peut compliquer la compréhension du code et entraîner des erreurs non évidentes si l'inférence de type échoue.
En Kotlin, le compilateur peut souvent déterminer le type d'une variable ou d'une expression en fonction du contexte. Cela fonctionne pour les variables, les valeurs de retour des fonctions et au sein d'expressions avec des lambdas. Cependant, il existe des situations où le compilateur exige une spécification explicite du type — par exemple, lors de la déclaration d'une fonction sans valeur de retour au sein d'une classe ('fun doSomething()') ou lorsque les expressions sont ambiguës.
Exemple de code :
val a = 42 // Int val s = "hello" // String fun sum(x: Int, y: Int) = x + y // le type de retour Int est inféré automatiquement val list = listOf(1, 2, 3) // List<Int> // Spécification explicite du type nécessaire si la valeur ne peut pas être déduite val emptyList: List<String> = emptyList() // sinon ce sera List<Nothing>
Caractéristiques clés :
Pourquoi ne peut-on pas toujours omettre le type après le deux-points, par exemple pour les propriétés de classe ?
Pour les propriétés, initialisées en dehors du lieu de déclaration (par exemple, via un getter ou dans un bloc init), le compilateur ne peut pas inférer le type automatiquement car il ne voit pas l'initialisateur.
class User { val fullName: String // Il est obligatoire de spécifier le type, sinon erreur get() = "name" }
Quel type aura une variable si on utilise emptyList() sans type explicite ?
Le type List<Nothing> sera inféré, ce qui rend le résultat pratiquement inutile.
val list = emptyList() // List<Nothing>
Quand l'inférence de type ne fonctionne-t-elle pas avec les paramètres de fonction ?
Dans la signature de la fonction, il est toujours nécessaire de spécifier explicitement les types des paramètres, sinon le compilateur renverra une erreur.
// Erreur : // fun foo(x) = x * 2 // Correct : fun foo(x: Int) = x * 2
Un développeur utilise emptyList() pour retourner une valeur d'une fonction API, sans spécifier le type. En conséquence, le type List<Nothing> est obtenu, ce qui pose des problèmes lors de l'utilisation de cette API.
Avantages :
Un développeur spécifie toujours explicitement le type lorsqu'il travaille avec des collections vides et dans les situations où cela améliore la lisibilité, et dans les autres cas, il se fie à l'inférence de type du compilateur.
Avantages :