Kotlin progresse magistralement sur l'indice de popularité des langages de programmation : PyPL. Soit 4 places de gagnées et 12ième position sur le podium !
Vous avez lu le titre ?
Je veux dire
Vous avez lu le titre ???
I'm sooooooo happy ! Déjà parce que j'avais misé sur Kotlin il y bientôt 4 ans maintenant mais ce n'est pas tout ! Kotlin dispose de l'un des compilateurs les plus restrictifs du marché (avec celui de Rust) et cela signifie que plus ces langages se développeront et moins il sera possible à des non-dev de se faire passer pour des développeurs car il ne leur sera plus possible de passer la barrière du compilateur sans comprendre tous les mécanismes qui se passent derrière (et donc devenir de "vrais devs").
Pourquoi cet élitisme me direz-vous ?
Tout simplement parce que je suis à mon compte et que voir des sous-traitants qui mentent sur un CV et conséquemment tirent mes tarifs vers le bas alors que nous ne proposons pas du tout le même niveau de prestation, bah ça m'agace. Et je ne travaille pas pour être gentille avec le reste du monde, je suis en concurrence directe et brutale avec ceux qui ne m'aident pas à augmenter mon bénéfice. C'est ça, le "libre-marché", ce n'est pas quelque chose j'approuve dans le fond (ceux qui me lisent connaissent mon point de vue sur l'ignominie capitaliste), mais je ne vais pas nier la réalité du terrain et me priver des outils que les règles du jeu fabriquent et permettent.
Bref, non seulement Kotlin est un outil formidable du point de vue de l'ingénierie mais en plus Kotlin sera un outil formidable du point de vue du recrutement et de la sélection. Et ce n'est qu'un début, vous verrez :D
Un bel exemple d'exécution parallèle et de résultat synchrone en Kotlin.
@Animal je te recommande cet article pour mieux comprendre le travail que tu feras avec @Milk et @Sigmund.
Remarque :
- Le concept de cache ici pose problème puisque des méthodes qui transforment (à base de verbes) retournent en réalité quelque chose.
- Les méthodes avec des boolean ont des verbes, c'est une exception à la Yegorification du code.
// Usage :
object Main {
fun regularUseCase() {
// Given
val christmas = LocalDate(2019, 12, 25)
val calendar:HolidaysCalendar = HolidaysCalendarForYear(Year(2019))
// When
val daysOff:DateSet = calendar.daysOff()
// Then
println(daysOffs.contains(christmas)) // print true
}
fun cachedUseCase() {
// Given
val christmas = LocalDate(2019, 12, 25)
val cache:Cache = HolidaysCalendarCache()
// When
val daysOff2:HolidaysCalendar = cache.value(Year(2019))
val daysOff3:HolidaysCalendar = cache.value(Year(2019))
// Then
println(daysOff2.contains(christmas)) // print true
println(daysOff2 === daysOff3) // print true (same instance)
}
@JvmStatic
fun main() {
val main = Main()
main.regularUseCase()
main.cachedUseCase()
}
}
// Interfaces
interface DateSet {
/**
* Determine whether or not the specified date is in this set.
*
* @param date
* The date to research.
*
* @return true if the date exists in this set, false otherwise.
*/
fun contains(date:LocalDate):Boolean
/**
* Determine whether or not the specified date is in this set.
*
* @param date
* The date to research.
*
* @return true if the date exists in this set, false otherwise.
*/
fun contains(date:Calendar):Boolean
/**
* Return the current set of date as an iterable collection.
*
* @return A collection having all the date stored in this set.
*/
fun asCollection():Collection<LocalDate>
}
interface HolidaysCalendar {
/**
* Return a set of holiday dates.
*
* @return All holidays for a period (see implementation for more detail).
*/
fun daysOff():DateSet
/**
* The period covered by this calendar.
*
* @return Something in the CalenadrPeriod enumeration.
*/
fun range():CalenadrPeriod
}
class Cache<K, V> {
/**
* Determine whether or not the specified value exists in this cache.
*
* @param value
* The value to search.
*
* @return true if the value has been found, false otherwise.
*/
fun contains(value:V):Boolean
/**
* Determine whether or not a key exists in this cache.
*
* @param key
* The key to search.
*
* @return true if the key exists, false otherwise (reminder: a key cannot exists if linked to nothing).
*/
fun containsKey(key:K):Boolean
/**
* Retrieve the value related to the specified key.
*
* @param key
* The key related to the researched value.
*
* @return The value related to the given key.
*
* @throw UnexistingEntryException
* When the subsystem cached by this object is not able to restitute a value using the specified key.
*/
fun value(key:K):V
/**
* The list of keys used by this cache.
*
* @return The list of keys used by this cache.
*/
fun keys():List<K>
/**
* Remove the specified key in order to force an update.
*
* @param key
* The key of the cache entry to remove.
*/
fun remove(key:K)
/**
* Clear all entry is the current cache.
*/
fun reset()
}
// Implementation of HolidaysCalendar
class HolidaysCalendarForYear(private val year:Year):HolidaysCalendar
class HolidaysCalendarForMonth(private val year:Year, private val month:Month):HolidaysCalendar
// Implementation of Cache
class HolidaysCalendarCache:Cache<Year, HolidaysCalendar>
J'abonde dans le sens de l'article. Le design pattern Builder est totalement obsolète en Kotlin puisque ce langage intègre les "defaulted method parameters" ce qui fait que si un paramètre venait à manquer, alors il prendrait automatiquement la valeur par défaut, par exemple :
class SocketJavaX(
private val port:Int = 0,
private val host:String? = null,
private val ssl:Boolean = false
)
Quand je vous disais que Kotlin a une multitude de petites choses qui rendent le dev facile et magique.
Quand j'évoque Kotlin, je sais que comme beaucoup je mets en avant un langage en donnant une impression de fanatisme... Mais faut bien comprendre que je suis une grande fan !
Un exemple, la différence entre la @Deprecated
de Java et la @Deprecated
de Kotlin.
// En java nous déclarons une méthode dépréciée soit sans commentaire
@Deprecated
public void vieuxProut() {
// ...
}
// Soit avec
@Deprecated("Cette implémentation sera abandonnée dans la future 1.2. Utilisez Toto.petDansLaSoie() à la place")
public void vieuxProut() {
// ...
}
Mais en Kotlin nous avons bien plus !
@Deprecated(
"Cette implémentation sera abandonnée dans la future 1.2. Utilisez Toto.petDansLaSoie() à la place",
replaceWith = ReplaceWith(
"petDansLaSoie(x)",
"com.maboite.monprojet.Toto.petDansLaSoie"
)
)
fun vieuxProut() {
// ...
}
Et à cet instant IntelliJ va automatiquement proposer d'utiliser la nouvelle implémentation, par exemple :
Et ce langage est BARDÉ de petits détails comme ça qui vous facilitent la vie.
Bon ça fait deux années maintenant et je sais où j'en suis niveau langage de programmation : de toutes les syntaxes, ma préférée est sans aucun doute et de trèèès loin celle de Kotlin (sauf pour les get / set).
Par contre, le meilleur compilateur du marché est celui de Rust, il n'y a pas photo. J'ai vraiment hâte que Kotlin Native décolle 😉 !
Via Riduidel.
Spoiler de l'article : Kotlin gagne quasiment partout.
Par contre Kotlin n'est pas que pour Android mais aussi pour tout ce qui cible la JVM ou la compilation du byte-code de JVM en natif. Chez nous il est côté serveur depuis plus de deux ans maintenant et a TOTALEMENT REMPLACÉ JAVA !
Nouvelle version mineure de Kotlin apportant une pléthore de correctifs ! La release note générale est très bien faite (les animations aident vraiment comprendre) ! Bref un modèle à suivre.
En plus de tout ceci je vous mets :
- Le lien vers le change log JVM.
- Le lien vers le change log Kotlin Native.
J'étais complètement passée à côté de cela ! L'API time a été repensée en Kotlin pour éviter la confusion de passer une durée en Long et de ne pas savoir s'il s'agit de secondes, de millisecondes, de nanosecondes, etc.
L'idée est d'utiliser les inline-classes (ie. l'enrichissement d'un type existant par un sous-type) et c'est très astucieux regardez :
import kotlinx.coroutines.delay
import kotlin.time.*
@ExperimentalTime
suspend fun greetAfterTimeout(duration: Duration) {
delay(duration.toLongMilliseconds())
println("Hi!")
}
@UseExperimental(ExperimentalTime::class)
suspend fun main() {
greetAfterTimeout(100.milliseconds)
greetAfterTimeout(1.seconds)
}
Les Integer contiennent des classes internes qui vont retourner un objet de type Duration
et contenant la valeur de l'Integer. De cette manière nous avons la valeur et son type en une seule fois.
Ce langage est tellement réfléchi c'est incroyable.
Cela avait le don de me frustrer qu'une feature aussi standard ne soit toujours pas implémentée dans un IDE qui sait le faire dans d'autres langages depuis des années. Et après une courte recherche, il y a une raison plus que pertinente : l'ordre des imports en Kotlin influe sur la façon dont le code sera compilé.
Dit autrement, pour une même classe, ordonnancer ses imports différemment ne produira pas le même bytecode. De ce fait, j'ai désactivé le check dans Ktlint pour éviter tout problème.
P.S : IntelliJ semble importer les éléments "dans le bon ordre par défaut".
La documentation en Kotlin est produite à partir de Dokka et non le maven-javadoc-plugin (normal puisqu'il s'agit de parler/lexer un autre langage).
Cette page décrit les différences de syntaxe entre la JavaDoc et la KDoc.
La page est très limpide sur les Do et les Don't. Je suis en train d'intégrer le plugin à nos builds.
lol @Doudou qui parle des coroutines et qui ne lit pas mes posts.
En vrai, c'est trop cool les coroutines tu verras !
Ohhhh ce benchmark sur Kotlin ! Sur certains points Kotlin est plus rapide que Java mais pas tout le temps et il y a des choses à éviter absolument comme les varargs
.
Pour résumer l'article :
Plus rapide que Java | Mois rapide que Java |
---|---|
✅ Higher-order functions | ⛔️ Varargs + Spread Operator |
✅ Lambda expressions | ⛔️ Delegate Properties |
✅ Companion Objects | ⛔️ Ranges (forEach function) |
✅ Local Functions | |
✅ Null safety | |
✅ Indirect access to Range | |
✅ Ranges (local, non-primitive types) | |
✅ Ranges (iteration with explicit step) | |
✅ Use of indices on custom collection |
Pour @Animal, voici l'opérateur when
en deux exemples tirés de la doc officielle (cf. lien principal).
Le simple :
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
Le compliqué :
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
Ceux qui me lisent le savent, je trouve que Rust est un très bon langage mais je lui reproche quand même certaines choses :
- Il n'est pas trivial d'écrire des classes (comme il l'est en Kotlin).
- Le code à écrire est aussi volumineux que celui de Java (Kotlin est 30% à 40% plus concis).
- Il utilise autant d'abréviations stupides que C++.
- Certains éléments de syntaxe sont une immondice à mi chemin entre C++ et O-Caml/Ruby (typiquement le pipe "|" pour les lambas, en dix ans je n'ai jamais pu m'y faire, ou encore l'opérateur "->" pour définir un type de retour d'une fonction).
Mon langage préféré serait à 100% Kotlin si celui-ci disposait :
- D'une cross-compilation native.
- D'un garbage collector injecté au compile-time à l'image de Rust et l'emploi du Ownership.
Oui, c'est ce qu'il faudrait à Kotlin, j'ai vraiment hâte de voir ce que vont donner GraalVM et Kotlin native dans les prochains mois.
Via Riduidel
Toute une documentation très bien faite sur les coroutines en Kotlin et les mécanismes de synchronisation que le langage fourni. Vraiment très bien pour apprendre à tirer partie des CPU multi-coeurs.
Je me souviens il y a presque trois ans déjà, avoir passé une soirée avec Chlouchloutte à essayer de comprendre comment marchait la lazy evaluation des streams en Kotlin.
Cet article nous aurait bien aidé.
La solution avec Jackson est extrêmement élégante :
Les dépendances :
dependencies {
compile 'com.fasterxml.jackson.core:jackson-databind:2.7.1-1'
compile 'com.fasterxml.jackson.module:jackson-module-kotlin:2.7.1-2'
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.7.1'
}
Créer les entités qui vont recevoir la configuration Yaml :
data class UniverseSizeDto(val maxGalaxies: Int, val maxSystems: Int, val maxPlanets: Int)
data class ResourcesDto(val crystal: Int, val gas: Int, val energy: Int)
data class StarterPlanetDto(val resources: ResourcesDto)
data class ConfigDto(val universeSize: UniverseSizeDto, val starterPlanet: StarterPlanetDto, val roundTime: Int)
Le code de chargement du fichier :
fun loadFromFile(path: Path): ConfigDto {
val mapper = ObjectMapper(YAMLFactory()) // Enable YAML parsing
mapper.registerModule(KotlinModule()) // Enable Kotlin support
return Files.newBufferedReader(path).use {
mapper.readValue(it, ConfigDto::class.java)
}
}