Was Java-Programmierer mit Kotlin nützlich finden

Kotlin wird die offizielle Sprache für Android sein wurde auf der Goole I / O 2017 angekündigt. Dieser Beitrag richtet sich an Java-Programmierer und beschreibt einige nützliche Kotlin-Funktionen, über die Java nicht verfügt.

Dieser Beitrag ist so geschrieben, dass er unabhängig verstanden werden kann, aber er ist der dritte in der folgenden Reihe. Es wird davon ausgegangen, dass Sie die grundlegende Syntax von Kotlin verstehen. Wenn Sie also die Syntax von Kotlin selbst nicht kennen, lesen Sie bitte zuerst den vorherigen Beitrag.

  1. Fast das gleiche wie Java
  2. Ein Ort, an dem Sie eine neue Denkweise brauchen und zum Stolpern neigen
  3. ** Bequeme Dinge, die nur Kotlin bieten ← In diesem Beitrag behandelte Inhalte **

Praktische Dinge, die nur Kotlin bieten

Beschreibt einige nützliche Funktionen von Kotlin, die Java nicht hat.

Es heißt "einzigartig für Kotlin", aber es ist nur ein Vergleich mit Java. Dieser Beitrag richtet sich an Java-Programmierer. Viele sagen, es ist in anderen Sprachen (wenn auch nicht in Java).

Ich habe viel erwähnt, aber mein Favorit ist die Datenklasse und das letzte "Anwenden". Es ist wirklich praktisch.

String Template

Dies wird in anderen Sprachen häufig als Zeichenfolgeninterpolation bezeichnet.

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

Sie können den Wert der Variablen "foo" in der Zeichenfolge "$ foo" in die Zeichenfolge einbetten. Sie können auch einen beliebigen Ausdruck in den Teil "..." schreiben, indem Sie wie "$ {...}" schreiben. Es ist nicht erforderlich, Zeichenfolgen wie in Java mit "+" zu kombinieren.

Mehrzeiliges String-Literal

Kotlin erlaubt mehrzeilige String-Literale. Dies ist nützlich, um Zeichenfolgenliterale mit Zeilenumbrüchen und "wie ↓ zu erstellen. Sie können auch Zeichenfolgenvorlagen wie" $ title "in mehrzeiligen Zeichenfolgenliteralen verwenden.

// 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)

↓ ist das Ausgabeergebnis.

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

Funktion

Mit Kotlin können Sie im Gegensatz zu Java klassenunabhängige ** _ Funktionen _ ** erstellen. Diejenigen, die Java noch nicht kennen, kennen den Begriff Funktion möglicherweise nicht, aber er ähnelt einer unabhängigen Methode, die keiner Klasse oder Instanz angehört. Seine Rolle ähnelt der statischen Methode von Java.

Zum Beispiel ist Kotlins listOf eine Funktion. Da es nicht zu einer Klasse gehört, ist im Gegensatz zur statischen Methode "Arrays.asList" kein Teil von "Arrays" erforderlich.

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

Häufig verwendete Funktionen wie "listOf" und "mapOf" sind nützlich, um ohne einen solchen Klassennamen aufgerufen zu werden (selbst in Java kann "import static" dies tun, aber wenn Sie es nicht wiederholt verwenden, "import") Es ist ein Ärger zu tun).

Im Gegensatz zur statischen Methode haben Funktionen keine separaten Namespaces nach Klassennamen. Wenn Sie also etwas zu einer Funktion machen, treten Namenskonflikte auf. Es ist jedoch praktisch, wenn Sie es nicht missbrauchen, indem Sie eine "private" Funktion nur in einer Datei oder eine "interne" Funktion nur in einem bestimmten Modul erstellen.

Single-Expression function

Eine Funktion, die mit einem einzelnen Ausdruck implementiert werden kann (nicht nur eine Funktion, sondern auch eine Methode), kann kurz beschrieben werden, wie in ↓ gezeigt.

// Kotlin
//Normales Schreiben
fun square(number: Int): Int {
  return number * number
}
// Kotlin
//Prägnantes Schreiben
fun square(number: Int): Int = number * number

Der Code kann komprimiert werden und ist viel sauberer.

Funktionsart und Zuordnung

Sie können einer Variablen auch eine Funktion zuweisen.

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

Beachten Sie, dass der Typ von "Quadrat" "(Int) -> Int" ist. Dies bedeutet eine Funktion, die ein "Int" als Argument verwendet und "Int" zurückgibt. Wenn Sie "(Foo, Bar) -> Baz" schreiben, ist dies der Funktionstyp, der "Foo" als erstes Argument und "Bar" als zweites Argument empfängt und "Baz" zurückgibt.

Das Einfügen in eine Variable kann auch als Argument übergeben werden.

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

Standardargument

Mit Kotlin können Sie Standardargumente angeben, anstatt Funktionen und Methoden zu überladen.

// 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 {
  ...
}

Sie können auch mehrere Standardargumente angeben und nur eines davon angeben.

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

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

Überlastung des Bedieners

Tatsächlich sind Kotlin-Operatoren Methoden. Zum Beispiel ist "+" eine Methode namens "plus", und das Schreiben von "1 + 2" entspricht dem Schreiben von "1.plus (2)". Fügen Sie den Modifikator "operator" hinzu, um eine Methode zu implementieren, die als Operator fungiert.

// 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)

Die Entsprechung zwischen den Operatoren und Methoden der vier Regeln ist wie folgt.

Operator Methode
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)

Weitere Einzelheiten finden Sie im offiziellen Dokument.

Diejenigen, die ich besonders nützlich finde, sind "get" und "set". Jedes entspricht [] (eines Arrays in Java).

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

Javas List und Maps get / set ( put) waren nervig, aber in Kotlin wurden dank der Überladung des Operators Sammlungselemente wie[i]gesammelt (In Kotlin können Sie einen Eintrag auch mit der Methode "set" in "Map" registrieren.)

Destructuring Declaration

Manchmal möchten Sie zwei Werte vorübergehend koppeln und behandeln, aber nicht genug, um eine Klasse dafür zu erstellen. In Java können Sie immer nur einen Methodenrückgabewert zurückgeben, aber Sie wollten möglicherweise zwei Rückgabewerte einmal zurückgeben. In einem solchen Fall hatte ich keine andere Wahl, als eine weinende Vereinbarung zu treffen oder eine spezielle Klasse zu gründen.

Kotlin hat immer einen Rückgabewert, stellt jedoch eine generische Klasse namens "Pair <out A, out B>" bereit.

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

Der Vorteil dieses "Paares" besteht darin, dass es als ↓ zugewiesen werden kann.

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

Dies wird als ** Destructuring Declaration ** bezeichnet. Wenn Sie in diesem Fall eine Methode erstellen, die "Pair" zurückgibt, können Sie festlegen, dass sie sich so verhält, als gäbe es zwei Rückgabewerte.

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

val (a, b) = foo()

Tatsächlich ist diese Funktion kein proprietäres Patent von "Pair". Jede Klasse kann diese Funktionalität mithilfe von Klassen mit "Operator Fun" wie "Komponente1", "Komponente2" implementieren.

// 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")

Natürlich kann es in zwei oder mehr Werte zerlegt und zugewiesen werden. Zusätzlich zu "Pair" bietet Kotlins Standardbibliothek eine Klasse mit drei Werten, "Triple".

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

Beachten Sie jedoch, dass der Missbrauch von "Pair" und "Triple" den Typ bedeutungslos macht und die Lesbarkeit beeinträchtigt. Wenn Sie Werte als "Paar" oder "Dreifach" behandeln, anstatt sie nur vorübergehend als Rückgabewert zu verwenden, sollten Sie überlegen, ob es besser ist, sie durch Ihre eigene Klasse zu ersetzen.

Destrukturierungsdeklarationen können auch in Kombination mit der for-Schleife verwendet werden. Wenn Sie beispielsweise in Kotlin ein Element von "List" in einer "for" -Schleife abrufen, können Sie den Index wie folgt zusammen mit diesem abrufen:

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

Der Rückgabetyp der Methode "withIndex" ist "Iterable <IndexedValue >". Da IndexedValue <T> component1 und component2 implementiert, kann es abgerufen werden, während der Wert wie in ↑ gezeigt zerlegt wird.

Sie können auch "Map" iterieren, um Schlüssel und Werte abzurufen.

// 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

Dies liegt daran, dass die "Iterator" -Methode von "Map <K, V>" Iterator <Map.Entry <K, V >> "zurückgibt und" component1 "von" Map.Entry <K, V> "der Schlüssel ist. Dies liegt daran, dass component2 einen Wert zurückgibt. Der vom Iterator "next" zurückgegebene "Map.Entry <K, V>" wird nacheinander "(Schlüssel, Wert)" zugewiesen, und die Schleife wird ausgeführt.

Darüber hinaus wird das obige "to" tatsächlich von "Pair" erzeugt. "a" bis 2 "entspricht dem Schreiben von" a "bis (2)". In Kotlin können Methoden, die mit dem Modifikator "infix" deklariert wurden, auf diese Weise in neutraler Notation aufgerufen werden. Da "a" bis 2 "=" a ".to (2)" = "Pair (" a ", 2)" ist, wird der Teil von "mapOf" unter Verwendung des Konstruktors von "Pair" wie folgt geschrieben. kann auch tun.

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

Da "A" und "B" von "Paar <A, B>" keine separate Beziehung von "A" zu "B" haben, wird angenommen, dass dieses "zu" zusammen mit "mapOf" verwendet wird. Ich denke es wurde getan. Die meisten Sprachen haben oft spezielle Literale und Syntax für "Map", aber Kotlin versucht, so viel wie möglich mit der Syntax der Sprache zu lösen, und "to" ist eine davon. Es ist eins.

Data class

Es war einfach, den primären Konstruktor zu verwenden, um die Deklaration und Implementierung des Konstruktors, der Felder, des Getters und des Setters zusammenzustellen. Aber mit ** Data Class ** können Sie es noch einfacher machen.

Es gibt viele Klassen, die nur Getter und Setter haben und nur die Werte als Daten enthalten. Eine solche Klasse wird in Kotlin als Datenklasse bezeichnet.

In solchen Klassen gibt es neben Konstruktoren und Eigenschaften häufig Routineimplementierungen von "equals", "hashCode" und "toString". In Java werden diese Codes durch die Funktion von IDE generiert, in Kotlin fügen Sie der Klassendeklaration lediglich "Daten" als Modifikator hinzu. Dann werden die Implementierungen von "hashCode", "equals" und "toString" hinter den Kulissen erstellt und können so behandelt werden, als ob sie existieren.

// 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)

Darüber hinaus generiert Data Class automatisch Methoden wie "component1" und "component2" für jede Eigenschaft. Daher können Sie die Destructuring Declaration auch für eine Instanz der Datenklasse verwenden.

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

Und das Nützlichste an Data Class ist die Kopiermethode.

In der modernen Programmierung ist die Idee, dass das, was unveränderlich sein kann, so unveränderlich wie möglich sein sollte. Veränderbare Instanzen verursachen bei der Freigabe häufig Probleme. Daher müssen Sie je nach Bedarf eine Kopie zurückgeben. Wenn Sie jedoch eine Kopie der Instanz zurückgeben und der darin enthaltene Wert eine veränderbare Instanz ist, müssen Sie sie auch in einer Kettenreaktion kopieren (freigegeben ohne eine tiefe Kopie anstelle einer flachen Kopie). Wir können nicht garantieren, dass eine veränderbare Instanz keine Probleme verursacht. Um eine solche Komplexität zu vermeiden, ist es einfacher, die Teile, die unveränderlich sein können, von den Teilen zu trennen, die veränderlich sein müssen, und die unveränderliche Welt mit unveränderlich zu vervollständigen.

Selbst in der unveränderlichen Welt gibt es Fälle, in denen Sie nur einen Teil einer Instanz ändern möchten. Wenn Sie eine Instanz einer unveränderlichen Klasse ändern möchten, drücken Sie die Änderung aus, indem Sie die neue Instanz mit den Änderungen neu erstellen.

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

Wie in ↑ gezeigt, ist es schwierig, alle Eigenschaften an den Konstruktor zu übergeben, um nur eine Eigenschaft zu ändern. Es könnte tolerierbar sein, wenn es ungefähr 3 Eigenschaften gibt, aber was ist, wenn es 20 Eigenschaften gibt? Es ist einfacher, mit der Kopiermethode der Datenklasse zu schreiben.

Die folgende Kopiermethode wird automatisch in der Datenklasse generiert.

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

Da die Standardargumente festgelegt sind, müssen Sie nur die Eigenschaften angeben, die Sie ändern möchten.

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

Dies unterscheidet sich nicht wesentlich vom Ändern einer veränderlichen Instanz mit dem Setter. ** Es ist großartig, gleichzeitig Unveränderlichkeit und einfache Veränderung zu erreichen **.

Extension

Wenn Sie ein objektorientiertes Sprachprogramm schreiben, möchten Sie möglicherweise einer vorhandenen Klasse eine Methode hinzufügen.

// Kotlin
(3 + 2) * (3 + 2) //Ich möchte das Berechnungsergebnis quadrieren`+`
(3 + 2).square() // `Int`Zu`square`Ich wünschte ich hätte eine Methode

Natürlich können Sie anstelle einer Methode eine Funktion erstellen und das Objekt als erstes Argument übergeben.

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

square(3 + 2)

Wenn Sie jedoch mehrere Prozesse mit einer Funktion verketten möchten, werden die Reihenfolge der Prozesse und die Reihenfolge im Code umgekehrt, was unpraktisch ist.

// Kotlin
//In der Reihenfolge foo → bar → baz → qux ausführen
qux(baz(bar(foo(x))))

Methoden können der Reihe nach verkettet werden.

// Kotlin
//In der Reihenfolge foo → bar → baz → qux ausführen
x.foo().bar().baz().qux()

Mit Kotlin können Sie Ihren Klassen später Methoden (wie) hinzufügen. Dieser Mechanismus heißt ** Extension **.

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

(3 + 2).square()

Die von Extension hinzugefügten Methoden unterscheiden sich jedoch von regulären Methoden.

Gewöhnliche Methoden werden zur Laufzeit dynamisch aufgelöst, wobei die Implementierung tatsächlich aufgerufen wird.

// 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`Aber`Animal`Wenn`2` 、 `Cat`Wenn`3`

Die von Extension hinzugefügten Methoden werden jedoch zur Kompilierungszeit statisch aufgelöst. Polymorphismus ist nicht möglich.

// 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`Aber`Animal`Aber`Cat`Aber`2`

Die Erweiterung schreibt die Klasse selbst nicht neu. Der Inhalt der durch Extension hinzugefügten Methode ist eine Funktion. Eine Funktion, die wie eine solche Methode aussieht, heißt ** Extension Function **. Das Verhalten des Codes in ↑ ist leicht zu verstehen, wenn Sie ihn als gewöhnliche Funktion wie ↓ betrachten.

// 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`Aber`Animal`Aber`Cat`Aber`2`

Welcher der überladenen foos aufgerufen wird, wird zur Kompilierungszeit bestimmt. Da "Tier" vom Typ "Tier" ist, wird durch den Typ des an das Argument übergebenen Werts (in diesem Fall "Tier") bestimmt, ob es sich um "Tier" oder "Katze" handelt. Getan werden.

Warum macht Extension diese verwirrende Sache, ohne die Klasse selbst neu zu schreiben? Wenn Sie die Klasse selbst neu schreiben und zufällig eine Methode mit demselben Namen zwischen verschiedenen Bibliotheken hinzufügen, kommt es zu Konflikten bei der Implementierung der Methode. Wenn die Bibliotheken A und B derselben Klasse eine Methode namens "foo" hinzufügen, wird die Methode "foo" in Bibliothek A möglicherweise unerwartet in Bibliothek B aufgerufen. Das ist beängstigend und ich kann Extension nicht verwenden. Eine solche Verwirrung kann nicht auftreten, wenn sie zur Kompilierungszeit statisch behoben wird.

Es ist praktisch, die Erweiterungsfunktion in Form einer Methode aufrufen zu können, aber es kann verwirrend sein, dass sich das Verhalten ändert, je nachdem, ob es sich um eine echte Methode oder eine Erweiterungsfunktion handelt, obwohl es von der Benutzerseite gleich aussieht. Fügen Sie in einer Oberklasse und einer Unterklasse keine Erweiterungsfunktion mit demselben Namen hinzu, da dies verwirrend sein kann.

object

In einigen Fällen, wie bei einem Singleton, benötigt eine Klasse nur eine Instanz. Kotlin macht es einfach, einen Singleton zu erstellen, indem das Schlüsselwort "Objekt" anstelle von "Klasse" verwendet wird.

// 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

Es mag wie die statische Methode in Java aussehen, weil es Foo.bar () ist, aber Foo ist eine Klasse und eine Instanz. Als Beweis dafür können Sie einer Variablen vom Typ "Foo" auch "Foo" zuweisen.

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

Es ist einfach, so etwas wie "Foo.getInstance ()" nicht zu tun, und im Gegensatz zu einer Klasse, die nur eine "statische" Methode hat, ist "Objekt" auch eine Instanz, sodass Sie von anderen Klassen erben können. Es ist praktisch, weil Sie sogar Polymorphismus machen können.

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

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

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

Beachten Sie, dass Kotlin dieses "Objekt" verwendet, um eine anonyme innere Klasse zu erstellen. Dies liegt daran, dass es immer nur eine Instanz der anonymen inneren Klasse gibt.

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

Übrigens kann Kotlin auch eine SAM-Konvertierung wie Java 8 verwenden, sodass der obige Code in Wirklichkeit wie folgt präzise geschrieben werden kann.

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

Sealed Class

Möglicherweise möchten Sie eine Klasse oder Schnittstelle erstellen und auf diese Klasse beschränken, da sie vererbt / implementiert werden kann.

Angenommen, Sie erstellen einen Typ, der JSON zuordnet. Die JSON-Spezifikation ist in hier klar definiert, und die einzigen akzeptierten JSON-Werte sind Objekt, Array, Zeichenfolge, Zahl, Wahr, Falsch und Null. Lassen Sie uns dies mit einem Typ ausdrücken.

Angenommen, sie werden durch "JSONObject", "JSONArray", "JSONString", "JSONNumber", "JSONTrue", "JSONFalse" bzw. "JSONNull" dargestellt. Betrachten Sie die JSONValue-Schnittstelle als ihren Supertyp.

// 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

Beachten Sie, dass "JSONTrue", "JSONFalse", "JSONNull" "Objekt" und nicht "Klasse" sind. Instanzen dieser Typen entsprechen dem JSON "true", "false" bzw. "null" und sind "object", da es nur einen gibt.

Dies ermöglicht es jedoch jemandem, später etwas wie "Klasse JSONFoo: JSONValue" hinzuzufügen. Da die JSON-Spezifikation nur die oben genannten sieben Arten von JSON-Werten zulässt, möchte ich verhindern, dass andere Klassen "JSONValue" implementieren. In Java können Sie solche Einschränkungen nicht durchsetzen, in Kotlins ** Sealed Class ** jedoch.

// 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()

Sealed Class kann nur aus derselben Datei geerbt werden, in der sie deklariert wurde. Daher kann ein Dritter die Unterklasse nicht ohne Erlaubnis implementieren.

Da die Arten von Unterklassen begrenzt sind, funktioniert es gut mit einer umfassenden Verzweigung nach Typ unter Verwendung von "Wann".

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

Wenn Sie mit "is" verzweigen, kann Smart Cast "json" als überprüften Typ im Teil von "{...}" behandeln. Zum Beispiel kann im "{...}" Teil von "JSONObject-> {...}" "json" als "JSONObject" -Typ behandelt werden.

Beachten Sie, dass Sealed Class eine Klasse und keine Schnittstelle ist. Sie können die versiegelte Klasse selbst jedoch nicht instanziieren, da sie automatisch zu einer abstrakten Klasse wird. Sie können nur die abstrakte Methode im Sealed Class-Body definieren und als Schnittstelle verwenden, oder Sie können eine allgemeine Implementierung schreiben.

Bereichsfunktion

Kotlin verfügt über eine Reihe nützlicher Funktionen, die als Bereichsfunktionen bezeichnet werden. Sie sind "auch", "anwenden", "lassen", "laufen", "mit". Obwohl die Namensbereichsfunktion sowohl auf Japanisch als auch auf Englisch verwendet wird, konnte sie im offiziellen Dokument nicht bestätigt werden. → (Ergänzung) [Basierend auf dem offiziellen Blog](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).

Für die Scope-Funktion @ngsw_taro ausführliche Erklärung, daher halte ich es hier für besonders nützlich, "let" und "apply" Ich werde nur etwa vorstellen.

let

Angenommen, Sie möchten etwas wie "x + 1 / x" tun. Wenn nun "x" durch "foo (42)" gefunden wird, kann der Code zur Berechnung von "x + 1 / x" wie folgt geschrieben werden:

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

Es gibt jedoch ein Problem mit diesem Code. Das heißt, ich habe foo (42) zweimal ausgeführt. Wenn foo ein sehr schwerer Prozess ist, ist es sinnlos, dieselbe Berechnung zweimal durchzuführen. Wenn sich der Prozess jedes Mal ändert, wenn er wie "Math.random ()" ausgeführt wird, ändert sich das Berechnungsergebnis. Um dies zu vermeiden, müssen Sie das Ergebnis von "foo (42)" einmal einer Variablen zuweisen.

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

Dies ist jedoch unpraktisch, da es unmöglich wird, in einen Ausdruck zu schreiben, wenn die Zuordnung zur Variablen eingefügt wird. Let ist in einem solchen Fall nützlich. let ist eine Erweiterungsfunktion, die für jeden Typ in der Standardbibliothek implementiert ist und wie folgt verwendet werden kann.

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

Die [Implementierung] von let (https://github.com/JetBrains/kotlin/blob/1.1.2/libraries/stdlib/src/kotlin/util/Standard.kt#L62) lautet wie folgt ..

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

Das heißt, es übergibt sich an die als Argument übergebene Funktion, und der Rückgabewert dieser Funktion ist der Rückgabewert von "let" selbst.

Wie oben erwähnt, ist "let" praktisch, wenn Sie das Berechnungsergebnis mehrmals in einer Formel verwenden möchten.

Darüber hinaus ist es bequem, es in Kombination mit "?" Zu verwenden, wie in Letzte Einführung gezeigt.

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

Der obige Lambda-Ausdruck "{it * it}" wird nur ausgeführt, wenn "x" nicht "null" ist. Wenn "x" "3" ist, wird "Quadrat" zu "9". Wenn andererseits "x" "null" ist, ist es "x?", Also wird "let" selbst nicht ausgeführt und das Ergebnis ("square") ist "null". Dies ist das Äquivalent von "map" und "flatMap" in Javas "Optional".

apply

Last but not least ist dies meine Lieblings-Bewerbung.

Für eine Klasse mit vielen zu setzenden Parametern möchten Sie möglicherweise eine Instanz erstellen und dann den Setter wiederholt lesen, um sie einzurichten. "Übernehmen" ist in solchen Fällen nützlich.

Zum Beispiel ist ↓ der Code aus dem JavaFX Oracle Tutorial (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();

Ich rufe wiederholt "Setter" auf und der Code scheint redundant zu sein, und die Hierarchie ist verwirrend und unübersichtlich.

Das Umschreiben in Kotlin mit apply ergibt:

// 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()
}

In dem an "apply" übergebenen Lambda-Ausdruck besteht der Punkt darin, dass Sie auf "title" zugreifen können, ohne "primaryStage" zu durchlaufen, z. B. "title =" Hello World! "". Dadurch wird vermieden, dass wiederholt auf Mitglieder in Form von "primaryState" zugegriffen wird. Dies liegt daran, dass in dem an "apply" übergebenen Lambda-Ausdruck "primaryStage", der Empfänger von "apply", als "this" behandelt wird. Dies kann durch Kotlins Verknüpfungsmechanismus mit dem Empfänger als "dies" im Funktionsliteral erfolgen. Weil.

Da der Rückgabewert von "apply" der Empfänger selbst ist ("primaryStage" für "primaryStage.apply {...}"), sollten Sie auch die temporären Variablen wie "btn" und "root" im Java-Code entfernen. Ich kann. Dies ist von Fall zu Fall leicht zu verstehen. Wenn Sie jedoch eine temporäre Variable erstellen möchten, können Sie sie auch in Kotlin erstellen. Ich weise temporären Variablen nur zu, wenn es schwierig ist zu sagen, was ich ohne Namen mache, und es wahrscheinlich die Lesbarkeit des Codes beeinträchtigt.

apply ist implementiert wie folgt:

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

Dieses T. () -> Unit`` T. ordnet den Empfänger this zu.

Wie man Kotlin in Zukunft lernt

Dieser Beitrag soll Java-Programmierern den reibungslosen Einstieg in Kotlin erleichtern und ist keine vollständige Erklärung. Wenn Sie Kotlin umfassender lernen möchten, empfehle ich Ihnen, die offiziellen Site-Dokumente zu verwenden. Obwohl es auf Englisch ist, denke ich, dass Android-App-Entwickler normalerweise Englisch lesen, und da es einfach Englisch ist, glaube ich nicht, dass es ein großes Problem gibt.

Recommended Posts

Was Java-Programmierer mit Kotlin nützlich finden
Problem beim Finden von javax.annotation.Generated in Java 11 nicht
Suchen Sie eine Teilmenge in Java
[Java] KClass in Java herunterladen [Kotlin]
Ich habe versucht herauszufinden, was sich in Java 9 geändert hat
Bean-Mapping nützlich in Java ModelMapper
Zusammenfassung der Punkte, die Java-Programmierer beim ersten Lesen der Kotlin-Quelle als rutschig empfinden
Was ist eine Klasse in der Java-Sprache (3 /?)
Was ist eine Klasse in der Java-Sprache (1 /?)
Was ist eine Klasse in der Java-Sprache (2 /?)
Was ist die Hauptmethode in Java?
HMAC in Kotlin
Partisierung in Java
Änderungen in Java 11
Janken in Java
Was ist Java <>?
Was ich in Java gelernt habe (Teil 2) Was sind Variablen?
Was ist java
Was ich getan habe, als ich Java zu Kotlin konvertiert habe
Was mich schon immer an Java Final interessiert hat
Umfangsrate in Java
Wo Java-Programmierer dazu neigen, über Kotlin zu stolpern
Wie schreibe ich Java String # getBytes in Kotlin?
Was Java-Unerfahrene getan haben, um Kotlin zu studieren
FizzBuzz in Java
[Java] KFunction von Method / Constructor in Java abrufen [Kotlin]
Was ist besser, Kotlin oder zukünftiges Java?
Durchsuchen Sie Klassenobjekte in Kotlin (anstelle der Java-Klasse name.class).
Schreiben Sie eine Klasse in Kotlin und nennen Sie sie in Java
Was ich in Java gelernt habe (Teil 3) Anweisung zur Ausführung von Anweisungen
Was ich beim Erstellen eines Servers in Java gelernt habe
Lesen Sie JSON in Java
Interpreter-Implementierung durch Java
Machen Sie einen Blackjack mit Java
Janken App in Java
Einschränkungsprogrammierung in Java
Setzen Sie Java8 in Centos7
Verbinden Sie Arrays in Java
"Hallo Welt" in Java
Aufrufbare Schnittstelle in Java
Kommentare in der Java-Quelle
Azure funktioniert in Java
Formatieren Sie XML in Java
Einfache HTML-Spezialchars in Java
Was ist Java-Kapselung?
Große Dezimalstelle in Kotlin
Boyer-Moore-Implementierung in Java
Hallo Welt in Java
Verwenden Sie OpenCV mit Java
WebApi-Memorandum mit Java
[Java, Kotlin] Typabweichung
Typbestimmung in Java
Befehle in Java ausführen (Ping)
Verschiedene Threads in Java
Zabbix API in Java
ASCII-Kunst in Java
Listen in Java vergleichen
POST JSON in Java
Was ist Java-Technologie?
Fehler in Java ausdrücken