Introduction à Kotlin
Introduction à Kotlin
- Langage moderne créé par JetBrains
- 100% interopérable avec Java
- Fonctionne sur la JVM
- Adopté officiellement pour Android
- Réduit significativement le boilerplate code
Kotlin en Action
// Java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
} .
public String getName() {
return name;
}
}// Kotlin
data class Person(
val name: String,
val age: Int
)
// Usage
val person = Person("John", 30)
println(person.name)Différences Clés avec Java
- Déclaration de Variables
- Type après le nom :
val name: String(Kotlin) vsString name(Java) - Variables immuables :
val(final) vs mutables :var
val name: String = "John" // Immutable (comme final en Java) var age: Int = 25 // Mutable (peut être modifié) - Type après le nom :
- Syntaxe Simplifiée
- Point-virgule optionnel en fin de ligne
- Inférence de type avec
valetvar
val message = "Hello" // Type String inféré var count = 0 // Type Int inféré - Data Classes
- Génération automatique des méthodes utilitaires
data class User(val name: String, val age: Int) val user = User("John", 30) val copy = user.copy(age = 31) // Copie avec modification val (name, age) = user // Destructuration - Null Safety
- Types non-nullables par défaut
- Opérateurs de sécurité pour les nullables
var name: String = "John" // Non-nullable var nullableName: String? = null // Nullable val length = nullableName?.length ?: 0 // Safe call avec Elvis - Expression Bodies
- Fonctions en une ligne
- Propriétés calculées
fun double(x: Int) = x * 2 val isAdult get() = age >= 18
Expresssions bodies
class Calculator {
// Expression body avec type de retour explicite
fun add(a: Int, b: Int): Int = a + b
// Expression body avec type inféré
fun multiply(a: Int, b: Int) = a * b
// Expression body pour une propriété en lecture seule
val version = "1.0"
// Expression body pour une propriété calculée
val isReady: Boolean
get() = true
// Expression body pour une propriété avec accesseurs personnalisés
var lastOperation: String = ""
private set
get() = "Last: $field"
}
fun main() {
val calc = Calculator()
println("2 + 3 = ${calc.add(2, 3)}")
println("2 * 3 = ${calc.multiply(2, 3)}")
println("Version: ${calc.version}")
println("Ready: ${calc.isReady}")
}
main()Getters et Setters en Kotlin
En Kotlin, la gestion des propriétés de classe est grandement simplifiée par rapport à Java. Par défaut, chaque propriété déclarée génère automatiquement un getter et un setter (pour les propriétés mutables var). Kotlin permet également de personnaliser ces accesseurs pour ajouter de la logique métier, des validations, ou des calculs. Les propriétés peuvent être en lecture seule (val), avoir des accesseurs personnalisés, utiliser des backing fields, ou même être calculées à la demande. Cette approche réduit considérablement le code boilerplate tout en maintenant un niveau élevé d’encapsulation.
class Temperature {
// Propriété avec getter/setter par défaut
var celsius: Double = 0.0
// Propriété avec getter/setter personnalisé
var fahrenheit: Double = 32.0
get() = celsius * 9/5 + 32
set(value) {
celsius = (value - 32) * 5/9
}
// Propriété en lecture seule (getter uniquement)
val kelvin: Double
get() = celsius + 273.15
// Backing field protégé avec getter public
private var _data: String = ""
val data: String
get() = _data
}
// Usage
fun main() {
val temp = Temperature()
temp.celsius = 25.0
println("Fahrenheit: ${temp.fahrenheit}") // 77.0
println("Kelvin: ${temp.kelvin}") // 298.15
}Membres Statiques en Kotlin
En Kotlin, il n’existe pas de mot-clé static comme en Java. À la place, Kotlin propose deux mécanismes principaux pour implémenter des fonctionnalités similaires :
- Le companion object qui permet de définir des membres liés à une classe (équivalent aux méthodes/propriétés statiques Java)
- La déclaration object qui crée un singleton avec tous ses membres “statiques”
Ces deux approches offrent plus de flexibilité que le simple mot-clé static de Java, tout en permettant une meilleure encapsulation et la possibilité d’implémenter des interfaces. Pour l’interopérabilité avec Java, l’annotation @JvmStatic permet de générer de véritables membres statiques.
// Companion object - équivalent des membres statiques
class MathUtils {
companion object {
const val PI = 3.14159
fun square(x: Int): Int = x * x
@JvmStatic // Pour utilisation depuis Java
fun cube(x: Int): Int = x * x * x
}
}
// Object declaration - Singleton avec membres statiques
object Configuration {
const val VERSION = "1.0.0"
var apiKey: String = ""
fun initialize() {
println("Configuration initialized")
}
}
// Usage
fun main() {
println(MathUtils.PI)
println(MathUtils.square(4))
Configuration.apiKey = "xyz"
Configuration.initialize()
}Null Safety
//| error: true
// Variables non-nullables par défaut
var name: String = "John"
//name = null // Erreur de compilation// Variables nullables explicites
var nullableName: String? = "John"
nullableName = null // OK
// Safe calls
println(nullableName?.length)
// Elvis operator
val len = nullableName?.length ?: 0Avantages
- NullPointerException évités à la compilation
- Gestion explicite des nulls
- Opérateurs de sécurité intégrés
- Vérification statique du null-safety
Extensions Functions
// Extension function
fun String.addHello(): String {
return "Hello $this"
}
// Usage
val name = "John"
println(name.addHello())
// Output: Hello John
// Extension property
val String.lastChar: Char
get() = this[length - 1]Bénéfices
- Étendre des classes existantes
- Améliorer la lisibilité
- Organiser le code
- Réutilisation facilitée
Smart Casts
fun printLength(obj: Any) {
if (obj is String) {
// obj est automatiquement casté en String
println(obj.length)
}
}
// Pattern Matching avec when
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long number"
!is String -> "Not a string"
else -> "Unknown"
}Avantages
- Casts automatiques sûrs
- Pattern matching puissant
- Vérification exhaustive
- Code plus concis
Coroutines
// Suspend function
// Fonction asynchrone
suspend fun fetchUser(): User {
delay(1000) // non bloquant
return User("John")
}
// Usage
// Appel asynchrone
GlobalScope.launch {
val user = fetchUser()
println(user)
}
// Flow
// Flux de données asynchrone
flow {
for (i in 1..3) {
delay(100)
emit(i)
}
}.collect { value ->
println(value)
}Caractéristiques
- Asynchrone léger
- Séquentiel en apparence
- Annulation structurée
- Gestion des erreurs simplifiée
Classe en Kotlin
Types de Classes
// Classe standard
class User(val name: String)
// Data class
data class Person(
val name: String,
val age: Int
)
// Classe abstraite
abstract class Shape {
abstract fun area(): Double
}
// Object (singleton)
object Config {
const val VERSION = "1.0"
}
// Companion object
class Factory {
companion object {
fun create() = Factory()
}
}Caractéristiques
- Classes
finalpar défaut - Constructeur primaire dans l’en-tête
- Initialiseurs avec
init - Propriétés dans le constructeur
- Héritage explicite avec
open - Délégation avec
by - Visibilité par défaut:
public
Exemple Complet
open class Animal(val name: String) {
open fun makeSound() = "..."
}
class Dog(
name: String,
val breed: String
) : Animal(name) {
override fun makeSound() = "Woof!"
init {
println("Dog $name created")
}
}
// Usage
fun main() {
val dog = Dog("Rex", "Labrador")
println("${dog.name} says: ${dog.makeSound()}")
}Points Clés
- Constructeur Primaire
- Partie de la déclaration de classe
- Initialiseurs avec
init - Paramètres peuvent être propriétés
- Héritage
- Classes
finalpar défaut - Mot-clé
openpour permettre l’héritage overrideexplicite obligatoire
- Classes
- Visibilité
publicpar défautprivate,protected,internal- S’applique aux classes et membres
Interface en Kotlin
// Interface basique
interface Movable {
fun move()
val speed: Int
// Méthode avec implémentation par défaut
fun describe() = "Moving at $speed"
}
// Interface avec propriété par défaut
interface Named {
val name: String
get() = "Unknown"
}
// Implémentation
class Car : Movable, Named {
override val speed = 100
override val name = "Ferrari"
override fun move() = println("Vrooom!")
}Caractéristiques
- Peuvent contenir des propriétés abstraites
- Méthodes par défaut possibles
- Implémentation multiple autorisée
- Pas d’état (pas de champs)
- Délégation possible avec
by
Genericité en Kotlin
// Classe générique basique
class Box<T>(var content: T)
// Variance déclarée
interface Source<out T> {
fun next(): T
}
// Contraintes de type
class NumberBox<T : Number>(var value: T)
// Type projection
fun copy(from: Array<out Any>, to: Array<Any>) {
from.forEachIndexed { i, e -> to[i] = e }
}
// Fonctions génériques
fun <T> printBox(box: Box<T>) {
println(box.content)
}Concepts Clés
- Variance:
in/out - Contraintes de type
- Projections de type
- Star projections
* - Reified type parameters
Propriétés en Kotlin
class User {
// Propriété simple
var name: String = ""
// Propriété en lecture seule
val id: Int = 1
// Propriété avec accesseurs
var age: Int = 0
get() = field
set(value) {
if (value >= 0) field = value
}
// Propriété calculée
val isAdult: Boolean
get() = age >= 18
// Propriété avec délégation
val lazy: String by lazy {
println("Computing...")
"Lazy Value"
}
}Fonctionnalités
- Backing field avec
field - Accesseurs personnalisables
- Délégation de propriétés
- Late initialization
lateinit - Propriétés calculées
Interopérabilité avec Java
Principes Fondamentaux
- Kotlin et Java peuvent coexister dans le même projet
- Appels transparents entre les deux langages
- Pas besoin d’adaptateurs ou de wrappers
- Compilation vers le même bytecode Java
De Java vers Kotlin
- Accès aux Classes Java
- Import direct des classes Java
- Utilisation naturelle des API Java
- Support complet des frameworks Java
- Conversion automatique des collections
- Adaptations Automatiques
- Getters/Setters → Propriétés Kotlin
- Interfaces SAM → Lambdas Kotlin
- Checked Exceptions → Gestion optionnelle
- Types primitifs → Types Kotlin
De Kotlin vers Java
- Fonctionnalités Kotlin en Java
- Properties → Getters/Setters
- Extension functions → Méthodes statiques
- Data classes → Classes Java standard
- Named parameters → Arguments standards
- Annotations Spéciales
@JvmStaticpour méthodes statiques@JvmFieldpour champs publics@JvmOverloadspour paramètres optionnels@JvmNamepour noms personnalisés
Considérations Importantes
- Null Safety
- Types nullables Kotlin → Types Java standards
- Types Java → Platform Types en Kotlin
- Vérifications de nullité à runtime
- Collections
- Collections Kotlin → Collections Java
- Mutabilité préservée
- Conversions automatiques
- Spécificités
- Extensions Kotlin → Méthodes statiques Java
- Coroutines → Compatibilité avec futures/callbacks
- Propriétés → Pattern JavaBean
Apprendre Kotlin
- Pour travailler :
- Kotlin Playground
- IntelliJ IDEA
- SDKMan:
sdk install kotlin
- Learning by exemple