Ce que les programmeurs Java trouvent utile avec Kotlin

Kotlin sera la langue officielle d'Android a été annoncé à Goole I / O 2017. Cet article est destiné aux programmeurs Java et décrit certaines fonctionnalités utiles de Kotlin que Java ne possède pas.

Cet article est écrit de manière à pouvoir être compris indépendamment, mais c'est le troisième de la série ci-dessous. Il est supposé que vous comprenez la syntaxe de base de Kotlin, donc si vous ne connaissez pas la syntaxe de Kotlin lui-même, veuillez d'abord lire l'article précédent.

  1. Presque identique à Java
  2. Un endroit où vous avez besoin d'une nouvelle façon de penser et avez tendance à trébucher
  3. ** Choses pratiques uniques à Kotlin ← Contenu couvert dans cet article **

Choses pratiques uniques à Kotlin

Décrit certaines fonctionnalités utiles de Kotlin que Java ne possède pas.

Il dit "unique à Kotlin", mais ce n'est qu'une comparaison avec Java. Cet article est destiné aux programmeurs Java. Beaucoup disent que c'est dans d'autres langues (mais pas en Java).

J'en ai beaucoup parlé, mais mon préféré est Data Class et le dernier que j'ai mentionné «appliquer». C'est vraiment pratique.

String Template

Ceci est souvent appelé interpolation de chaîne dans d'autres langues.

// Kotlin
val a = 2
val b = 3
println("$a + $b = ${a + b}") // 2 + 3 = 5

Vous pouvez incorporer la valeur de la variable «foo» dans la chaîne sous la forme «$ foo». Vous pouvez également écrire n'importe quelle expression dans la partie ... en écrivant comme $ {...}. Il n'est pas nécessaire de combiner des chaînes avec «+» comme en Java.

Littéral de chaîne multiligne

Kotlin autorise les littéraux de chaîne multi-lignes. Ceci est utile pour créer des chaînes de caractères avec des sauts de ligne et ", comme ↓. Vous pouvez également utiliser des modèles de chaînes comme" $ title "dans des chaînes de caractères sur plusieurs lignes.

// Kotlin
val title = "Hello, World!"
val headerId = "header"
val header = "Hello, World!"

val html = """<!DOCTYPE html>
<html>
  <title>$title</title>
  <body>
    <h1 id="$headerId">$header</h1>
  </body>
</html>
"""

println(html)

↓ est le résultat de la sortie.

<!DOCTYPE html>
<html>
  <title>Hello, World!</title>
  <body>
    <h1 id="header">Hello, World!</h1>
  </body>
</html>

une fonction

Kotlin vous permet de créer des fonctions indépendantes de la classe ** _ _ **, contrairement à Java. Ceux qui ne connaissent pas Java peuvent ne pas être familiers avec le terme fonction, mais c'est comme une méthode indépendante qui n'appartient pas à une classe ou à une instance. Son rôle est similaire à la méthode «statique» de Java.

Par exemple, «listOf» de Kotlin est une fonction. Comme il n'appartient pas à une classe, il ne nécessite pas une partie de ʻArrays.contrairement à la méthodestatic ʻArrays.asList.

// Java
List<Integer> list = Arrays.asList(2, 3, 5);
// Kotlin
val list = listOf(2, 3, 5)

Les fonctions fréquemment utilisées telles que listOf et mapOf sont utiles pour être appelées sans un nom de classe comme celui-ci (même en Java, ʻimport static peut le faire, mais ʻimport sauf s'il est utilisé à plusieurs reprises. C'est un problème à faire).

Contrairement à la méthode static, les fonctions n'ont pas d'espaces de noms séparés par nom de classe, donc si vous transformez quelque chose en fonction, des conflits de nom se produiront. Cependant, il est pratique de ne pas en abuser en créant une fonction «privée» uniquement dans un fichier ou une fonction «interne» limitée dans un certain module.

Single-Expression function

Une fonction qui peut être implémentée avec une seule expression (non seulement une fonction mais aussi une méthode) peut être décrite de manière concise comme indiqué dans ↓.

// Kotlin
//Écriture normale
fun square(number: Int): Int {
  return number * number
}
// Kotlin
//Rédaction concise
fun square(number: Int): Int = number * number

Le code peut être compressé et il est beaucoup plus propre.

Type de fonction et affectation

Vous pouvez également affecter une fonction à une variable.

// Kotlin
val square: (Int) -> Int = { it * it }
square(3) // 9

Notez que le type de square est (Int) -> Int. Cela signifie une fonction qui prend un ʻInt comme argument et retourne ʻInt. Si vous écrivez (Foo, Bar) -> Baz, ce sera le type de fonction qui reçoit Foo comme premier argument et Bar comme deuxième argument et renvoie Baz.

Le fait d'être placé dans une variable peut également être passé en argument.

val list = listOf(2, 3, 5)
list.map(square) // [4, 9, 25]

Argument par défaut

Kotlin vous permet de donner des arguments par défaut au lieu de surcharger les fonctions et méthodes.

// Java
static double log(double x, double base) {
  ...
}

static double log(double x) {
  return log(Math.E);
}
// Kotlin
fun log(x: Double, base: Double = Math.E): Double {
  ...
}

Vous pouvez également donner plusieurs arguments par défaut et n'en spécifier qu'un seul.

// Kotlin
fun foo(x: Int = 42, y: Boolean = true, z: String = "xyz") {
  ...
}

foo(y = false) // x = 42, z = "xyz"

Surcharge de l'opérateur

En fait, les opérateurs Kotlin sont des méthodes. Par exemple, «+» est une méthode appelée «plus», et écrire «1 + 2» équivaut à écrire «1.plus (2)». Pour implémenter une méthode qui agit comme un opérateur, ajoutez le modificateur ʻoperator`.

// Kotlin
class Vector2(val x: Double, val y: Double) {
  operator fun plus(vector: Vector2): Vector2 {
    return Vector2(x + vector.x, y + vector.y)
  }
}

Vector2(2.0, 3.0) + Vector2(4.0, 5.0) // Vector2(6.0, 8.0)

La correspondance entre les opérateurs et les méthodes des quatre règles est la suivante.

opérateur Méthode
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)

Pour plus de détails, veuillez consulter Documents officiels.

Ceux que je trouve particulièrement utiles sont «get» et «set». Chacun correspond à «[]» (d'un tableau en Java).

// Kotlin
val a = list[i] // list.get(i)
list[i] = b // list.set(i, b)

Les éléments Java List et Map`` get / set ( put) étaient ennuyeux, mais dans Kotlin, grâce à la surcharge des opérateurs, des éléments de collection comme[i] (Dans Kotlin, vous pouvez également enregistrer une entrée avec la méthode set dans Map).

Destructuring Declaration

Parfois, vous souhaitez associer temporairement deux valeurs et les traiter, mais pas suffisamment pour créer une classe pour cela. En Java, vous ne pouvez toujours renvoyer qu'une seule valeur de retour de méthode, mais vous avez peut-être voulu renvoyer deux valeurs de retour une fois. Dans un tel cas, je n'avais pas d'autre choix que de faire un arrangement pleurant ou de créer une classe dédiée.

Kotlin a toujours une valeur de retour, mais fournit à la place une classe générique appelée Pair <out A, out B>.

// Kotlin
val x = Pair(42, "xyz")

La commodité de cette «paire» est qu'elle peut être assignée comme ↓.

// Kotlin
val (a, b) = x
println(a) // 42
println(b) // "xyz"

Cela s'appelle ** Destructuring Declaration **. Dans ce cas, si vous créez une méthode qui retourne Pair, vous pouvez la faire se comporter comme s'il y avait deux valeurs de retour.

// Kotlin
fun foo(): Pair<Int, String> {
  return Pair(42, "xyz")
}

val (a, b) = foo()

En fait, cette fonctionnalité n'est pas un brevet exclusif de «Pair». N'importe quelle classe peut implémenter cette fonctionnalité en utilisant des classes avec un «plaisir de l'opérateur» comme «composant1», «composant2».

// Kotlin
class Foo(val a: Int, val b: String) {
  operator fun component1(): Int {
    return a
  }

  operator fun component2(): String {
    return b
  }
}

val (a, b) = Foo(42, "xyz")

Bien sûr, il peut être décomposé en deux ou plusieurs valeurs et attribué. En plus de Pair, la bibliothèque standard de Kotlin fournit également une classe qui contient trois valeurs, Triple.

// Kotlin
val triple = Triple(42, "xyz", true)
val (a, b, c) = triple

Cependant, sachez que l'abus de «Pair» et «Triple» rendra le type dénué de sens et altérera la lisibilité. En particulier, si vous commencez à gérer les valeurs comme «Pair» ou «Triple» au lieu de les utiliser temporairement comme valeur de retour, pensez à les remplacer par votre propre classe.

Les déclarations de déstructuration peuvent également être utilisées en combinaison avec la boucle «for». Par exemple, dans Kotlin, lorsque vous récupérez un élément de List dans une boucle for, vous pouvez récupérer l'index avec lui comme suit:

// Kotlin
val list = listOf("a", "b", "c")
for ((index, element) in list.withIndex()) {
  println("$index : $element")
}
0 : a
1 : b
2 : c

Le type de retour de la méthode withIndex est ʻIterable <IndexedValue >. Puisque ʻIndexedValue <T> implémente component1 et component2, il peut être récupéré en décomposant la valeur comme indiqué dans ↑.

Vous pouvez également itérer Map pour récupérer des clés et des valeurs.

// Kotlin
val map = mapOf(
  "a" to 2,
  "b" to 3,
  "c" to 5
)

for ((key, value) in map) {
  println("$key : $value")
}
a : 2
b : 3
c : 5

Ceci est dû au fait que la méthode ʻiterator Map <K, V>renvoie ʻIterator <Map.Entry <K, V >>, et le component1 deMap.Entry <K, V> ʻest la clé. C'est parce que «composant2» renvoie une valeur. LeMap.Entry <K, V>retourné par l'itérateurnext est assigné à (clé, valeur) ʻun par un, et la boucle est exécutée.

De plus, le "to" ci-dessus est en fait généré par "Pair". «a» à 2 »équivaut à écrire« a ».to (2)». Dans Kotlin, les méthodes déclarées avec le modificateur ʻinfixpeuvent être appelées en notation neutre de cette manière. Puisque" a "to 2 = "a" .to (2) = Pair ("a", 2) , la partie de mapOf s'écrit comme suit en utilisant le constructeur de Pair`. peut aussi faire.

// Kotlin
val map = mapOf(
  Pair("a", 2),
  Pair("b", 3),
  Pair("c", 5)
)

Puisque «A» et «B» de «Pair <A, B>» n'ont pas de relation séparée de «A» à «B», on suppose que ce «to» est utilisé avec «mapOf». Je pense que cela a été fait. La plupart des langages ont souvent des littéraux et une syntaxe spéciaux pour Map, mais Kotlin a tendance à essayer de résoudre avec la syntaxe du langage autant que possible, et to est l'un d'entre eux. C'est un.

Data class

Il était facile d'utiliser le constructeur principal pour rassembler la déclaration et l'implémentation du constructeur, des champs, du getter et du setter. Mais avec ** Data Class **, vous pouvez le rendre encore plus facile.

Il existe de nombreuses classes qui n'ont que des getters et des setters, contenant uniquement des valeurs sous forme de données. Une telle classe est appelée une classe de données dans Kotlin.

De telles classes ont souvent des implémentations standard de ʻequals, hashCode et toStringen plus des constructeurs et des propriétés. En Java, ces codes sont générés par la fonction de l'EDI, mais dans Kotlin, vous ajoutez simplementdatacomme modificateur à la déclaration de classe. Ensuite, les implémentations dehashCode, ʻequals, toString sont créées dans les coulisses et peuvent être traitées comme si elles existaient.

// Java
public final class Person {
  private final String firstName;
  private final String lastName;
  private final int age;

  public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public int getAge() {
    return age;
  }

  @Override
  public int hashCode() {
    return ...;
  }

  @Override
  public boolean equals(Object object) {
    if (!(object instanceof Person)) {
      return false;
    }

    Person person = (Person)object;
    return firstName.equals(person.firstName) && lastName.equals(person.lastName) && age == person.age;
  }

  @Override
  public String toString() {
    return "Person(firstName=" + firstName + ", lastName=" + lastName + ", age=" + age + ")";
  }
}
// Kotlin
data class Person(val firstName: String, val familyName: String, val age: Int)

De plus, Data Class génère automatiquement des méthodes telles que «composant1» et «composant2» pour chaque propriété. Par conséquent, vous pouvez également utiliser la déclaration de destruction sur une instance de Data Class.

// Kotlin
val person = Person("Albert", "Einstein", 28)
val (firstName, familyName, age) = person

Et la chose la plus utile à propos de Data Class est la méthode copy.

Dans la programmation moderne, l'idée est que ce qui peut être immuable doit être aussi immuable que possible. Les instances mutables ont tendance à poser des problèmes lorsqu'elles sont partagées, vous devez donc renvoyer une copie en fonction de vos besoins. Cependant, si vous renvoyez une copie d'une instance et que la valeur qu'elle contient est une instance mutable, vous devez également la copier dans une réaction en chaîne (partagée sans copie profonde plutôt qu'une copie superficielle). Nous ne pouvons garantir qu'une instance mutable ne causera aucun problème). Pour éviter une telle complexité, il est plus simple de séparer les parties qui peuvent être immuables des parties qui doivent être mutables, et de compléter le monde immuable avec immuable.

Cependant, même dans le monde immuable, il existe des cas où vous ne souhaitez modifier qu'une partie d'une instance. Lorsque vous souhaitez modifier une instance d'une classe immuable, exprimez la modification en recréant la nouvelle instance avec les modifications.

// Kotlin
val person = Person("Albert", "Einstein", 28)
val newPerson = Person(person.firstName, person.lastName, person.age + 1)

Comme indiqué dans ↑, il est difficile de transmettre toutes les propriétés au constructeur juste pour changer une propriété. Cela peut être tolérable s'il y a environ 3 propriétés, mais que faire s'il y en a 20? Il est plus facile d'écrire en utilisant la méthode copy de Data Class.

La méthode suivante copy est automatiquement générée dans la classe de données.

// Kotlin
fun copy(firstName: String = this.firstName, lastName: String = this.lastName,
  age: Int = this.age) = User(firstName, lastName, age)

Étant donné que les arguments par défaut sont définis, il vous suffit de spécifier les propriétés que vous souhaitez modifier.

// Kotlin
val person = Person("Albert", "Einstein", 28)
val newPerson = person.copy(age = person.age + 1)

Ce n'est pas très différent du changement d'une instance mutable avec setter. ** C'est formidable de pouvoir atteindre l'immuabilité et la facilité de changement en même temps **.

Extension

Lors de l'écriture d'un programme de langage orienté objet, vous souhaiterez peut-être ajouter une méthode à une classe existante.

// Kotlin
(3 + 2) * (3 + 2) //Je veux mettre au carré le résultat du calcul`+`Est fait deux fois et est inutile
(3 + 2).square() // `Int`À`square`J'aurais aimé avoir une méthode

Bien sûr, vous pouvez créer une fonction au lieu d'une méthode et passer l'objet comme premier argument.

// Kotlin
fun square(value: Int): Int = value * value

square(3 + 2)

Cependant, si vous souhaitez concaténer plusieurs processus avec une fonction, l'ordre des processus et l'ordre dans le code sont inversés, ce qui n'est pas pratique.

// Kotlin
//Exécuter dans l'ordre foo → bar → baz → qux
qux(baz(bar(foo(x))))

Les méthodes peuvent être chaînées dans l'ordre.

// Kotlin
//Exécuter dans l'ordre foo → bar → baz → qux
x.foo().bar().baz().qux()

Kotlin vous permet d'ajouter des méthodes (comme) à vos classes plus tard. Ce mécanisme s'appelle ** Extension **.

// Kotlin
fun Int.square(): Int = this * this

(3 + 2).square()

Cependant, les méthodes ajoutées par Extension sont différentes des méthodes régulières.

Les méthodes ordinaires sont résolues dynamiquement au moment de l'exécution, implémentation qui est effectivement appelée.

// Kotlin
open class Animal {
  open fun foo(): Int = 2
}
class Cat: Animal() {
  override fun foo(): Int = 3
}

val animal: Animal = if (Math.random() < 0.5) Animal() else Cat()
animal.foo() // `animal`Mais`Animal`Si`2` 、 `Cat`Si`3`

Cependant, les méthodes ajoutées par Extension sont résolues statiquement au moment de la compilation. Le polymorphisme n'est pas possible.

// Kotlin
open class Animal
class Cat: Animal()

fun Animal.foo(): Int = 2
fun Cat.foo(): Int = 3

val animal: Animal = if (Math.random() < 0.5) Animal() else Cat()
animal.foo() // `animal`Mais`Animal`Mais`Cat`Mais`2`

L'extension ne réécrit pas la classe elle-même. La substance de la méthode ajoutée par Extension est une fonction. Une fonction qui ressemble à une telle méthode est appelée ** Extension Function **. Le comportement du code dans ↑ est facile à comprendre si vous le considérez comme une fonction ordinaire comme ↓.

// Kotlin
open class Animal
class Cat: Animal()

fun foo(animal: Animal): Int = 2
fun foo(cat: Cat): Int = 3

val animal: Animal = if (Math.random() < 0.5) Animal() else Cat()
foo(animal) // `animal`Mais`Animal`Mais`Cat`Mais`2`

Lequel des foos surchargés est appelé est déterminé au moment de la compilation. Puisque ʻanimal est de type ʻAnimal, qui foo est appelé, que ce soit ʻAnimal ou Cat, est déterminé par le type de la valeur passée à l'argument (dans ce cas, ʻAnimal). Sera fait.

Pourquoi Extension fait-elle cette chose déroutante sans réécrire la classe elle-même? Si vous réécrivez la classe elle-même, si vous ajoutez une méthode du même nom entre différentes bibliothèques, l'implémentation de la méthode sera en conflit. Si les bibliothèques A et B ajoutent toutes les deux une méthode appelée foo à la même classe, il est possible que la méthode foo de la bibliothèque A soit appelée de manière inattendue dans la bibliothèque B. C'est effrayant et je ne peux pas utiliser l'extension. Une telle confusion ne peut pas se produire si elle est résolue statiquement au moment de la compilation.

Il est pratique de pouvoir appeler la fonction d'extension sous la forme d'une méthode, mais il peut être déroutant que le comportement change selon qu'il s'agit d'une méthode réelle ou d'une fonction d'extension, même si cela semble identique du côté de l'utilisateur. N'ajoutez pas une fonction d'extension avec le même nom dans une superclasse et une sous-classe car cela peut prêter à confusion.

object

Dans certains cas, comme un singleton, une classe n'a besoin que d'une seule instance. Kotlin facilite la création d'un singleton en utilisant le mot-clé ʻobject au lieu de class`.

// Java
class Foo {
  private static final Foo INSTANCE = new Foo();

  private Foo() {}

  public static Foo getInstance() {
    return INSTANCE;
  }

  public int bar() {
    return 42;
  }
}

Foo.getInstance().bar(); // 42
// Kotlin
object Foo {
  fun bar(): Int = 42
}

Foo.bar() // 42

Cela peut ressembler à la méthode static en Java parce que c'est Foo.bar (), mais Foo est une classe et une instance. Pour preuve, vous pouvez également assigner Foo à une variable de type Foo.

// Kotlin
val foo: Foo = Foo
foo.bar() // 42

Il est facile de ne pas faire quelque chose comme Foo.getInstance (), et contrairement à une classe qui n'a qu'une méthode static, ʻobject` est aussi une instance donc vous pouvez hériter d'autres classes. C'est pratique car vous pouvez même faire du polymorphisme.

// Kotlin
interface Foo {
  fun bar(): Int
}

object Baz : Foo {
  override fun bar(): Int = 42
}

Baz.bar() // 42
val foo: Foo = Baz
foo.bar() // 42

Notez que Kotlin utilise cet «objet» pour créer une classe interne anonyme. C'est parce qu'il n'y a toujours qu'une seule instance de la classe interne anonyme.

// Java
button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    ...
  }
});
// Kotlin
button.setOnClickListener(object : View.OnClickListener {
  override fun onClick(view: View) {
    ...
  }
})

À propos, Kotlin peut également utiliser la conversion SAM comme Java 8, donc en réalité, le code ci-dessus peut être écrit de manière concise comme suit.

// Kotlin
button.setOnClickListener { view ->
  ...
}

Sealed Class

Vous pouvez créer une classe ou une interface et la limiter à cette classe car elle peut être héritée / implémentée.

Par exemple, supposons que vous créez un type qui mappe JSON. La spécification JSON est clairement définie dans here, et les seules valeurs JSON acceptées sont object, array, string, number, true, false et null. Exprimons cela avec un type.

Supposons qu'ils soient représentés respectivement par JSONObject, JSONArray, JSONString, JSONNumber, JSONTrue, JSONFalse et JSONNull. Et considérez l'interface JSONValue comme leur supertype.

// Kotlin
interface JSONValue
class JSONObject(val value: Map<String, JSONValue>) : JSONValue
class JSONArray(val value: List<JSONValue>) : JSONValue
class JSONString(val value: String) : JSONValue
class JSONNumber(val value: Double) : JSONValue
object JSONTrue : JSONValue
object JSONFalse : JSONValue
object JSONNull : JSONValue

Notez que JSONTrue, JSONFalse, JSONNull sont ʻobject, pas class`. Les instances de ces types correspondent respectivement aux JSON «true», «false» et «null» et sont «object» car il n'y en a qu'un.

Cependant, cela permet à quelqu'un d'ajouter quelque chose comme class JSONFoo: JSONValue plus tard. La spécification JSON n'autorise que les sept types de valeurs JSON ci-dessus, je veux donc empêcher d'autres classes d'implémenter JSONValue. Java ne peut pas appliquer de telles contraintes, contrairement à la ** _Sealed Class _ ** de Kotlin.

// Kotlin
sealed class JSONValue
class JSONObject(val value: Map<String, JSONValue>) : JSONValue()
class JSONArray(val value: List<JSONValue>) : JSONValue()
class JSONString(val value: String) : JSONValue()
class JSONNumber(val value: Double) : JSONValue()
object JSONTrue : JSONValue()
object JSONFalse : JSONValue()
object JSONNull : JSONValue()

La classe scellée ne peut être héritée qu'à partir du même fichier dans lequel elle a été déclarée. Par conséquent, un tiers ne peut pas implémenter la sous-classe sans autorisation.

Étant donné que les types de sous-classes sont limités, cela fonctionne bien avec un branchement exhaustif par type en utilisant «quand».

// Kotlin
val json: JSONValue = ...
when (json) {
  is JSONObject -> { ... }
  is JSONArray -> { ... }
  is JSONString -> { ... }
  is JSONNumber -> { ... }
  is JSONTrue -> { ... }
  is JSONFalse -> { ... }
  is JSONNull -> { ... }
}

Si vous branchez avec ʻis, Smart Cast peut traiter json comme un type coché dans la partie de {...}. Par exemple, dans la partie {...}de ʻis JSONObject-> {...}, json peut être traité comme un type JSONObject.

Notez que Sealed Class est une classe, pas une interface. Cependant, vous ne pouvez pas instancier la classe scellée elle-même car elle devient automatiquement une classe abstraite. Vous pouvez définir uniquement la méthode ʻabstract` dans le corps de la classe scellée et l'utiliser comme interface, ou vous pouvez écrire une implémentation commune.

Fonction de portée

Kotlin a un ensemble de fonctions utiles appelées fonctions de portée. ʻAlso, ʻapply, let, run, with. Cependant, bien que la fonction de portée de nom soit utilisée à la fois en japonais et en anglais, elle n'a pas pu être confirmée dans le document officiel. → (Ajout) [Original du blog officiel](http://qiita.com/ngsw_taro/items/d29e3080d9fc8a38691e#%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97% E9% 96% A2% E6% 95% B0% E3% 81% A8% E3% 81% 84% E3% 81% 86% E5% 90% 8D% E7% A7% B0% E3% 81% AB% E3% 81% A4% E3% 81% 84% E3% 81% A6).

Pour la fonction scope, @ngsw_taro explication détaillée, donc je pense que c'est particulièrement utile ici let et ʻapply` Je vais seulement présenter environ.

let

Disons que vous voulez faire quelque chose comme «x + 1 / x». Maintenant, si x est trouvé par foo (42), le code à calculer x + 1 / x peut être écrit comme suit:

// Kotlin
foo(42.0) + 1 / foo(42.0)

Cependant, il y a un problème avec ce code. Autrement dit, j'ai exécuté foo (42) deux fois. Si «foo» est un processus très lourd, il est inutile de faire deux fois le même calcul. Ou, si le processus change à chaque fois qu'il est exécuté comme Math.random (), le résultat du calcul changera. Pour éviter cela, vous devez affecter le résultat de foo (42) à une variable une fois.

// Kotlin
val x = foo(42.0)
x + 1 / x

Cependant, cela n'est pas pratique car il devient impossible d'écrire dans une expression si l'affectation à la variable est insérée. «Let» est utile dans un tel cas. let est une fonction d'extension implémentée pour n'importe quel type dans la bibliothèque standard et peut être utilisée comme suit.

// Kotlin
foo(42.0).let { x -> x + 1 / x }

La [mise en œuvre] de let (https://github.com/JetBrains/kotlin/blob/1.1.2/libraries/stdlib/src/kotlin/util/Standard.kt#L62) ressemble à ceci: ..

// Kotlin
inline fun <T, R> T.let(block: (T) -> R): R = block(this)

Autrement dit, il passe à la fonction passée en argument, et la valeur de retour de cette fonction est la valeur de retour de let lui-même.

Comme mentionné ci-dessus, «let» est pratique lorsque vous souhaitez utiliser le résultat du calcul plusieurs fois dans une formule.

De plus, il est pratique de l'utiliser en combinaison avec «?.» Comme indiqué dans Dernière introduction.

// Kotlin
val x: Int? = ...
val square: Int? = x?.let { it * it }

L'expression lambda ci-dessus {it * it} n'est exécutée que si x n'est pas null. Si «x» est «3», alors «carré» devient «9». D'un autre côté, si «x» est «nul», c'est «x». », Donc« let »lui-même n'est pas exécuté et le résultat (« carré ») est« nul ». C'est l'équivalent de map et flatMap dans ʻOptional` de Java.

apply

Dernier point mais non des moindres, ceci est ma «application» préférée.

Pour une classe avec de nombreux paramètres à définir, vous pouvez créer une instance, puis lire setter à plusieurs reprises pour la configurer. ʻApply` est utile dans de tels cas.

Par exemple, ↓ est le code extrait du tutoriel Oracle JavaFX (http://docs.oracle.com/javafx/2/get_started/hello_world.htm).

// Java
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent event) {
        System.out.println("Hello World!");
    }
});

StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();

J'appelle setter à plusieurs reprises et le code semble redondant, et la hiérarchie est confuse et encombrée.

Réécrire ceci dans Kotlin en utilisant ʻapply` donne:

// Kotlin
primaryStage.apply {
  title = "Hello World!"
  scene = Scene(StackPane().apply {
    children.add(Button().apply {
      text = "Say 'Hello World'"
      onAction = EventHandler<ActionEvent> { println("Hello World!") }
    })
  }, 300.0, 250.0)
  show()
}

Dans l'expression lambda passée à ʻapply, le fait est que vous pouvez accéder à titlesans passer parprimaryStage, comme title =" Hello World! ". Cela évite d'accéder à plusieurs reprises aux membres sous la forme de primaryState.. En effet, dans l'expression lambda transmise à ʻapply, primaryStage, le récepteur de ʻapply, est traité comme this`. Cela peut être fait par le [mécanisme de liaison] de Kotlin (https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver) avec le récepteur comme «this» dans le littéral de la fonction. Parce que.

De plus, puisque la valeur de retour de ʻapply est le récepteur lui-même (primaryStage pour primaryStage.apply {...} ), éliminez les variables temporaires telles que btn et root` dans le code Java. Je peux. C'est facile à comprendre au cas par cas, mais si vous souhaitez créer une variable temporaire, vous pouvez également la créer dans Kotlin. Je n'affecte des variables temporaires que lorsqu'il est difficile de dire ce que je fais sans nom et que cela risque de compromettre la lisibilité du code.

ʻApply` est implémenté comme suit:

// Kotlin
inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

Ce T. () -> Unit`` T. relie le récepteur à this.

Comment apprendre Kotlin à l'avenir

Cet article est destiné à aider les programmeurs Java à démarrer avec Kotlin en douceur et n'est pas une explication exhaustive. Si vous voulez apprendre Kotlin de manière plus complète, je vous recommande d'utiliser les Documents officiels du site. Bien que ce soit en anglais, je pense que les développeurs d'applications Android lisent généralement l'anglais, et comme c'est un anglais simple, je ne pense pas qu'il y ait un gros problème.

Recommended Posts

Ce que les programmeurs Java trouvent utile avec Kotlin
Problème de ne pas trouver javax.annotation.Généré en Java 11
Rechercher un sous-ensemble en Java
[Java] Obtenir KClass en Java [Kotlin]
J'ai essayé de découvrir ce qui avait changé dans Java 9
Bean mapping utile dans Java ModelMapper
Résumé des points que les programmeurs Java trouvent glissants lorsqu'ils lisent la source Kotlin pour la première fois
Qu'est-ce qu'une classe en langage Java (3 /?)
Qu'est-ce qu'une classe en langage Java (1 /?)
Qu'est-ce qu'une classe en langage Java (2 /?)
Quelle est la méthode principale en Java?
HMAC à Kotlin
Partition en Java
Changements dans Java 11
Janken à Java
Qu'est-ce que Java <>?
Ce que j'ai appris en Java (partie 2) Que sont les variables?
Qu'est-ce que 'java
Ce que j'ai fait lorsque j'ai converti Java en Kotlin
Ce qui m'a toujours intéressé dans Java Final
Taux circonférentiel à Java
Où les programmeurs Java ont tendance à trébucher sur Kotlin
Comment écrire Java String # getBytes dans Kotlin?
Ce que des personnes inexpérimentées en Java ont fait pour étudier Kotlin
FizzBuzz en Java
[Java] Obtenir KFunction à partir de la méthode / du constructeur en Java [Kotlin]
Quel est le meilleur, Kotlin ou futur Java?
Parcourir les objets de classe dans Kotlin (au lieu de Java class name.class)
Ecrire une classe en Kotlin et l'appeler en Java
Ce que j'ai appris en Java (partie 3) Déclaration d'exécution des instructions
Ce que j'ai appris lors de la création d'un serveur en Java
Lire JSON en Java
Implémentation de l'interpréteur par Java
Faites un blackjack avec Java
Application Janken en Java
Programmation par contraintes en Java
Mettez java8 dans centos7
Joindre des tableaux en Java
"Hello World" en Java
Interface appelable en Java
Commentaires dans la source Java
Fonctions Azure en Java
Formater XML en Java
Simple htmlspecialchars en Java
Qu'est-ce que l'encapsulation Java?
Grand décimal à Kotlin
Implémentation Boyer-Moore en Java
Hello World en Java
Utiliser OpenCV avec Java
Mémorandum WebApi avec Java
[Java, Kotlin] Variance de type
Détermination de type en Java
Exécuter des commandes en Java (ping)
Divers threads en java
API Zabbix en Java
Art ASCII à Java
Comparer des listes en Java
POST JSON en Java
Qu'est-ce que la technologie Java?
Exprimer l'échec en Java