Fonctions infixes dans Kotlin

Fonctions infixes dans Kotlin

1. introduction

Kotlin est un langage qui ajoute de nombreuses nouvelles fonctionnalités permettant d'écrire du code plus propre et plus facile à lire.

Cela facilite considérablement la maintenance de notre code et permet un meilleur résultat final de notre développement. Infix notation est l'une de ces fonctionnalités.

2. Qu'est-ce qu'une notation Infix?

Kotlin permet d’appeler certaines fonctions sans utiliser le point et les parenthèses. These are called infix methods, and their use can result in code that looks much more like a natural language.

Ceci est le plus souvent vu dans la définition en ligneMap:

map(
  1 to "one",
  2 to "two",
  3 to "three"
)

“to” peut ressembler à un mot-clé spécial, mais dans cet exemple, il s'agit d'une méthodeto() exploitant la notation infixe et renvoyant unPair<A, B>.

3. Fonctions communes d'infixe de bibliothèque standard

Outre la fonctionto(), utilisée pour créer des instancesPair<A, B>, il existe d'autres fonctions définies commeinfix.

Par exemple, les différentes classes numériques -Byte,Short, Int, etLong - définissent toutes les fonctions binairesand(), or(), shl(), shr(), ushr(), etxor(), permettant des expressions plus lisibles:

val color = 0x123456
val red = (color and 0xff0000) shr 16
val green = (color and 0x00ff00) shr 8
val blue = (color and 0x0000ff) shr 0

La classeBoolean définit les fonctions logiquesand(), or() etxor() de la même manière:

if ((targetUser.isEnabled and !targetUser.isBlocked) or currentUser.admin) {
    // Do something if the current user is an Admin, or the target user is active
}

La classeString définit également les fonctionsmatch etzip comme infixe, permettant un code simple à lire:

"Hello, World" matches "^Hello".toRegex()

Il existe d'autres exemples que l'on peut trouver dans la bibliothèque standard, mais ils sont peut-être les plus courants.

4. Écriture de méthodes d'infixe personnalisées

Souvent, nous voulons écrire nos propres méthodes d'infixe. These can be especially useful, for example, when writing a Domain Specific Language for our application, permettant au code DSL d'être beaucoup plus lisible.

Plusieurs bibliothèques Kotlin l'utilisent déjà avec grand succès.

Par exemple, la bibliothèquemockito-kotlin définit certaines fonctions d'infixe -doAnswer,doReturn, etdoThrow — à utiliser lors de la définition du comportement simulé.

L'écriture d'une fonction infixe est un cas simple de trois règles suivantes:

  1. La fonction est définie sur un class ou est une méthode d'extension pour un class

  2. La fonction prend exactement un paramètre

  3. La fonction est définie à l'aide du mot-cléinfix

À titre d’exemple simple, définissons un frameworkAssertion simple à utiliser dans les tests. Nous allons autoriser les expressions qui se lisent bien de gauche à droite à l'aide des fonctions d'infixe:

class Assertion(private val target: T) {
    infix fun isEqualTo(other: T) {
        Assert.assertEquals(other, target)
    }

    infix fun isDifferentFrom(other: T) {
        Assert.assertNotEquals(other, target)
    }
}

This looks simple and doesn’t seem any different from any other Kotlin code. Cependant, la présence du mot-cléinfix nous permet d'écrire du code comme celui-ci:

val result = Assertion(5)

result isEqualTo 5 // This passes
result isEqualTo 6 // This fails the assertion
result isDifferentFrom 5 // This also fails the assertion

Immédiatement, c'est plus propre à lire et plus facile à comprendre.

Note that infix functions can also be written as extension methods to existing classes. Cela peut être puissant, car cela nous permet d’augmenter les classes existantes d’ailleurs - y compris la bibliothèque standard - pour répondre à nos besoins.

Par exemple, ajoutons une fonction à unString pour extraire toutes les sous-chaînes qui correspondent à une expression régulière donnée:

infix fun String.substringMatches(r: Regex) : List {
    return r.findAll(this)
      .map { it.value }
      .toList()
}

val matches = "a bc def" substringMatches ".*? ".toRegex()
Assert.assertEquals(listOf("a ", "bc "), matches)

5. Sommaire

Ce didacticiel rapide montre certaines des choses qui peuvent être faites avec les fonctions infixes, notamment comment utiliser certaines existantes et comment créer les nôtres pour rendre notre code plus propre et plus facile à lire.

Comme toujours, des extraits de code peuvent être trouvés surover on GitHub.