Kotlin, est un language de programmation orienté objet et fonctionnel. Crée en 2011 par Jetbrains, il est aujourd'hui le second language le plus utilisé pour créer des applications tournant sur la JVM (Java Virtual Machine) derrière Java. Cependant, il peut également être compilé en JavaScript. Par ailleurs, en 2019, Google a annoncé que Kotlin était maintenant le language de programmation officielement supporté sur Android.
Soutenu par le géant américain Google, Kotlin a fait ses preuves et va continuer de grandir. De plus en plus de technologies telles que Spring commencent elles aussi à supporter ce language, mais pourquoi ? Kotlin a été crée d'améliorer le temps de production et la faciliter de développement. Il montre avant tout ses preuves par sa syntaxe, rapide, clair et efficace. Il a également une librairie standard plus complète. Tout comme Java il peut être utilisé pour créer des applications tournant sur la JVM. De plus, toutes les librairies Java sont importables sur Kotlin, c'est à dire qu'il n'y a rien à modifier.
Afin de migrer un projet ou d'en créer un nouveau en Kotlin, nous allons utiliser deux build tools au choix (Maven, Gradle) largement répandus dans le milieu des développeurs Java.
Ici nous allons nous séparer en deux catégories, gradle par défaut ou gradle avec l'extension kts officiellement crée pour Kotlin.
Lien vers leur documentation !
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.5.20'
}
group = "net.pseudow.tutorial"
version = "1.0.0"
sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect") // Une reflection pour Kotlin
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // La librairie standard de Kotlin
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = "11"
}
}
Lien vers leur documentation !
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.5.20"
}
group = "net.pseudow.tutorial"
version = "1.0.0"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect") // Une reflection pour Kotlin
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // La librairie standard de Kotlin
}
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "11"
}
}
Lien vers leur documentation !
<properties>
<kotlin.version>1.5.21</kotlin.version>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
</properties>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
Documentation officielle de Kotlin
Kotlin contrairement à Java contient plus de différents types de "classes". Nous avons par exemple les fichiers, similaires à des scripts, on peut y mettre des fonctions ou des classes etc... Afin de print un Hello, World nous allons utiliser un fichier parce que la fonction main dans Kotlin doit être en dehors d'une classe !
(Attention, il faut écrire le code dans un dossier kotlin et non java) Main.kt:
package net.pseudow.tutorial
fun main(args: Array<String>) = println("Hello, World!")
Il vous suffit d'éxecuter ce code maintenant, en effet comme vous pouvez le voir la syntaxe de kotlin est rapide, tous les "objets", que ce soit des méthodes ou des classes ou des fields sont de base public, vous n'avez donc pas besoin d'écrire public fun, évidemment il y a d'autres modifiers d'accès comme private ou protected. Ensuite les méthodes sont définies par le mot clef fun, et pour expliciter un type il faut écrire : Type. Pour finir quand la méthode ne contient qu'une expression on peut oublier les { } et directement écrire =.
Une fois spigot rajouté à vos dépendances Gradle ou Maven, nous allons écrire un petit code histoire de bien vous montrer la syntaxe de ce language:
KotlinTutorial.kt
class KotlinTutorial: JavaPlugin() {
/* En effet pour extendre une classe d'une autre il faut utiliser les :
après la classe et rajouter le () pour le constructeur qui ici est vide.
Si vous vouliez implementer une interface il vous aurait fallu faire la même chose sans ()
class KotlinTutorial: MonAbstractClass("uneValeurDuConstructor"), MonInterface
*/
lateinit var accountManager: AccoutManager
override fun onEnable() {
this.accountManager = AccoutManager(this)
this.accountManager.sayHi()
MyListener(this)
}
override fun onDisable() {
}
}
AccountManager.kt
import java.util.UUID
class AccountManager(private val plugin: KotlinTutorial) {
fun getRankName(playerId: UUID): String = "Salut"
fun sayHi() {
this.plugin.server.scheduler.runTask(this.plugin) {
println("Hi!")
}
}
}
MyListener.kt
class MyListener(private val plugin: KotlinTutorial) {
init {
this.plugin.server.pluginManager.registerEvents(this, this.plugin)
}
@EventHandler
private fun playerLogin(event: PlayerJoinEvent) {
val player = event.player
val account = Accout(playerName = player.name,
playerId = player.uniqueId)
player.sendMessage("Welcome ${player.name}!")
this.plugin.accountManager.saveAccount(account)
}
}
Account.kt
class Account(
val playerName: String,
var coins = .0f,
val playerId: Int
)
Essayons de comprendre la logique derrière. Kotlin est fait de base pour éviter les NullPointerException. Chaque valeur doit être initialisée immédiatement, soit dans un constructeur, soit en la définissant directement. Si vous avez besoin de la définire plus tard vous avez deux possibilités:
- Le keyword: lateinit Le mot clef lateinit veut littéralement dire que votre valeur va être initié plus tard. Les lateinit variables doivent toujours être déclarée avec un var (variable qui est réassignable) et non par un val (variable non réassignable). Cela peut causer des problèmes dans une librairie où vous ne voulez pas que la valeur soit modifiable dans ce cas-ci, Kotlin nous donne deux propriétés en plus, la propriété get et set:
lateinit var accountManager: AccoutManager
get() = it // De base vous ne la mettez pas mais c'est pour vous montrer
set(value) {
// Avant que la valeur soit assignée, nous allons soit rien faire soit nous pouvons créer
// une erreur pour avertir l'utilisateur
throw Exception("This value cannot be modified!")
}
- Le Delegates
var monInteger by Delegates<Int>.notNull()
Voici quelques exemples pour faire un petit plugin. Evidemment, les librairies sont traduites selon les conventions de Kotlin qui sont avec des fields publiques ce qui est plus rapide.
Kotlin est un language plus avec une syntaxe plus profitante que Java sur le long terme. Il provide aussi d'autres choses tel qu'une implémentation parfaite pour les corountines avec les suspend function, les DSL, les Sealed Class ou les extensions functions:
Vous définissez une extend function n'import où ça peut être dans un fichier et elle sera accessible partout !
fun String.print() = println(it)
val monString = "Salut"
monString.print()
// On peut donc faire plein de choses comme:
fun Player.getGuild(): Guild {
return quelqueChose
}
@EventHandler
private fun playerJoin(event: PlayerJoinEvent) {
val player = event.player
player.getGuild().leave()
}
Regardez-moi cette syntaxe, juste WOAW:
fun <T, R> Collection<T>.fold(
initial: R,
combine: (acc: R, nextElement: T) -> R // Une fonction qui prend en param, un objet de type R et T, et qui rend un objet de type R
): R {
var accumulator: R = initial
for (element: T in this) {
accumulator = combine(accumulator, element)
}
return accumulator
}
val items = listOf(1, 2, 3, 4, 5)
val product = items.fold(1, Int::times) // On va faire: 1x2 = 2, 2x2 = 4, 4x3 = 12, 12x5 = 120
println(product) // Output: 120