Skip to content

Instantly share code, notes, and snippets.

@Pseudow
Last active July 13, 2021 17:07
Show Gist options
  • Save Pseudow/5b41cf3d7cb9829e964fe0c7cbeaae5c to your computer and use it in GitHub Desktop.
Save Pseudow/5b41cf3d7cb9829e964fe0c7cbeaae5c to your computer and use it in GitHub Desktop.
Migrate from java to Kotlin

Comment migrer de Java à Kotlin ?


Introduction

Qu'est-ce que Kotlin ?

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.

Pourquoi Kotlin ?

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.


Migrer de Java à Kotlin

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.

Gradle

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.

gradle.build

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"
    }
}

gradle.build.kts

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"
    }
}

Maven

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>

Premier code en Kotlin

Documentation officielle de Kotlin

Hello, World!

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 =.

Premier plugin avec Spigot.

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:

  1. 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!")
    }
  1. 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.


Conclusion

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment