Kotlin ist eine moderne Programmiersprache, die Java-Bytecode kompiliert. Es ist kostenlos und Open Source und verspricht, die Kodierung für Android noch mehr Spaß zu machen.
Im vorigen Artikel haben Sie etwas über Klassen und Objekte in Kotlin erfahren. In diesem Lernprogramm erfahren Sie weiter mehr über Eigenschaften. Außerdem werden in Kotlin fortgeschrittene Arten von Klassen untersucht, indem Sie Folgendes untersuchen:
Wir können eine nicht-null-Eigenschaft in Kotlin als deklarieren spät initialisiert. Dies bedeutet, dass eine Nicht-NULL-Eigenschaft zum Zeitpunkt der Deklaration nicht mit einem Wert-Ist-Initialisierung über einen Konstruktor initialisiert wird. Stattdessen wird sie durch eine Methoden- oder Abhängigkeitsinjektion zu spät initialisiert.
Sehen wir uns ein Beispiel an, um diesen einzigartigen Eigenschaftsmodifizierer zu verstehen.
Klasse Presenter privates var-Repository: Repository? = null fun initRepository (Repo: Repository): Einheit this.repository = Repo Klasse Repository fun saveAmount (Betrag: Double)
In dem obigen Code haben wir eine veränderliche nullfähige Variable deklariert Repository
Eigenschaft, die vom Typ ist Repository
-in der Klasse Moderator
-Diese Eigenschaft haben wir dann während der Deklaration auf null gesetzt. Wir haben eine Methode initRepository ()
in dem Moderator
Klasse, die diese Eigenschaft später mit einem tatsächlichen Wert neu initialisiert Repository
Beispiel. Beachten Sie, dass dieser Eigenschaft auch ein Wert zugewiesen werden kann, indem ein Abhängigkeitsinjektor wie Dagger verwendet wird.
Nun können wir dazu Methoden oder Eigenschaften aufrufen Repository
In diesem Fall müssen wir eine Nullprüfung durchführen oder den Operator für sichere Anrufe verwenden. Warum? Weil der Repository
Eigenschaft ist vom Typ "nullable" (Repository?
). (Wenn Sie eine Auffrischung zur Nullfähigkeit in Kotlin benötigen, besuchen Sie bitte Nullability, Loops und Bedingungen.).
// Innerhalb der Presenter-Klasse Spaß sparen (Betrag: Double) Repository? .SaveAmount (Betrag)
Um nicht jedes Mal eine Nullprüfung durchführen zu müssen, wenn wir die Methode einer Eigenschaft aufrufen müssen, können wir diese Eigenschaft mit der lateinit
Modifikator - Dies bedeutet, dass wir diese Eigenschaft (die eine Instanz einer anderen Klasse ist) als deklariert hat spät initialisiert (dh die Eigenschaft wird später initialisiert).
Klasse Presenter privates lateinit var-Repository: Repository //…
Solange wir warten, bis der Eigenschaft ein Wert zugewiesen wurde, können wir sicher auf die Methoden der Eigenschaft zugreifen, ohne Nullprüfungen durchzuführen. Die Eigenschaftsinitialisierung kann entweder in einer Setter-Methode oder durch Abhängigkeitseingabe erfolgen.
repository.saveAmount (Betrag)
Wenn Sie versuchen, auf Methoden der Eigenschaft zuzugreifen, bevor sie initialisiert wurde, erhalten Sie eine kotlin.UninitializedPropertyAccessException
anstelle einer NullPointerException
. In diesem Fall lautet die Ausnahmemeldung "lateinit property repository wurde nicht initialisiert"..
Beachten Sie auch die folgenden Einschränkungen bei der Verzögerung einer Eigenschaftsinitialisierung mit lateinit
:
var
).Int
, Doppelt
, Schweben
, und so weiter. In Advanced Functions habe ich die vorgestellt in der Reihe
Modifikator für Funktionen höherer Ordnung - dies hilft bei der Optimierung von Funktionen höherer Ordnung, die ein Lambda als Parameter akzeptieren.
In Kotlin können wir dies auch verwenden in der Reihe
Modifikator für Eigenschaften. Mit diesem Modifikator wird der Zugriff auf die Eigenschaft optimiert.
Sehen wir uns ein praktisches Beispiel an.
class Student val nickName: String get () println ("Spitzname abgerufen") liefert "koloCoder" fun main (args: Array) val student = Student () print (student.nickName)
Im obigen Code haben wir eine normale Eigenschaft, Spitzname
, das hat nicht die in der Reihe
Modifikator. Wenn wir das Code-Snippet dekompilieren, verwenden Sie die Kotlin-Bytecode anzeigen Funktion (wenn Sie sich in IntelliJ IDEA oder Android Studio befinden, verwenden Sie Werkzeuge > Kotlin > Kotlin-Bytecode anzeigen), wird folgender Java-Code angezeigt:
public final class Student @NotNull public final String getNickName () String var1 = "Spitzname abgerufen"; System.out.println (var1); return "koloCoder"; public final class InlineFunctionKt public static final void main (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Student Student = neuer Student (); String var2 = student.getNickName (); System.out.print (var2);
In dem generierten Java-Code oben (einige Elemente des generierten Codes wurden der Kürze halber entfernt), können Sie das innerhalb der Main()
Methode der Compiler erstellt a Student
Objekt, genannt das getNickName ()
Methode und druckte dann den Rückgabewert.
Lassen Sie uns nun die Eigenschaft als angeben in der Reihe
Stattdessen und vergleichen Sie den generierten Bytecode.
//… inline val nickName: String //…
Wir legen einfach die in der Reihe
Modifikator vor dem Variablenmodifizierer: var
oder val
. Hier ist der für diese Inline-Eigenschaft generierte Bytecode:
//… public static final void main (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Student Student = neuer Student (); String var3 = "Nickname abgerufen"; System.out.println (var3); String var2 = "koloCoder"; System.out.print (var2); //…
Wieder wurde etwas Code entfernt, aber der Schlüssel ist der Main()
Methode. Der Compiler hat die Eigenschaft kopiert erhalten()
Funktionshauptteil und fügte es in die Aufrufstelle ein (dieser Mechanismus ähnelt Inline-Funktionen).
Unser Code wurde optimiert, da kein Objekt erstellt und die Eigenschafts-Getter-Methode aufgerufen werden muss. Aber, wie in den Inline-Funktionen beschrieben, hätten wir einen größeren Bytecode als zuvor - also mit Vorsicht.
Beachten Sie auch, dass dieser Mechanismus für Eigenschaften funktioniert, die kein Sicherungsfeld haben (Denken Sie daran, ein Sicherungsfeld ist nur ein Feld, das von Eigenschaften verwendet wird, wenn Sie diese Felddaten ändern oder verwenden möchten.).
In den erweiterten Funktionen habe ich auch Erweiterungsfunktionen erörtert - diese geben uns die Möglichkeit, eine Klasse mit neuen Funktionen zu erweitern, ohne von dieser Klasse erben zu müssen. Kotlin bietet auch einen ähnlichen Mechanismus für Eigenschaften an, genannt Erweiterungseigenschaften.
val String.upperCaseFirstLetter: String get () = thisSubstring (0, 1) .toUpperCase (). plus (thisSubstring (1))
Im Beitrag Erweiterte Funktionen haben wir a definiert GroßbuchstabenFirstLetter ()
Erweiterungsfunktion mit Empfängertyp String
. Hier haben wir es stattdessen in eine Erweiterungseigenschaft der obersten Ebene konvertiert. Beachten Sie, dass Sie in Ihrer Eigenschaft eine Getter-Methode definieren müssen, damit dies funktioniert.
Mit diesem neuen Wissen über Erweiterungseigenschaften wissen Sie also, dass Sie die Möglichkeit haben, eine Erweiterungseigenschaft dieser Klasse zu erstellen, wenn Sie jemals gewünscht haben, dass eine Klasse über eine nicht verfügbare Eigenschaft verfügen sollte.
Beginnen wir mit einer typischen Java-Klasse oder einem POJO (Plain Old Java Object)..
öffentliche Klasse BlogPost private final String title; private abschließende URI-URL; private abschließende String-Beschreibung; privates Enddatum publishDate; //… Konstruktor nicht der Kürze halber eingeschlossen @Override public boolean equals (Object o) if (this == o) return true; if (o == null || getClass ()! = o.getClass ()) gibt false zurück; BlogPost blogPost = (BlogPost) o; if (title! = null?! title.equals (blogPost.title): blogPost.title! = null) gibt false zurück; if (url! = null?! url.equals (blogPost.url): blogPost.url! = null) gibt false zurück; if (Beschreibung! = null?! description.equals (blogPost.description): blogPost.description! = null) gibt false zurück; return publishDate! = null? publishDate.equals (blogPost.publishDate): blogPost.publishDate == null; @Override public int hashCode () int result = title! = Null? title.hashCode (): 0; result = 31 * result + (url! = null? url.hashCode (): 0); result = 31 * result + (description! = null? description.hashCode (): 0); result = 31 * result + (publishDate! = null? publishDate.hashCode (): 0); Ergebnis zurückgeben; @Override public String toString () return "BlogPost " + "title = '" + title +' \ "+", url = "+ url +", + description + "\" + ", publishDate =" + publishDate + ''; //… Setter und Getter, die der Kürze halber auch ignoriert werden
Wie Sie sehen können, müssen wir die Klasseneigenschaften-Accessoren explizit codieren: Getter und Setter sowie Hash-Code
, gleich
, und toString
Methoden (IntelliJ IDEA, Android Studio oder die AutoValue-Bibliothek können uns dabei helfen, sie zu generieren). Wir sehen diesen Boilerplate-Code meistens in der Datenschicht eines typischen Java-Projekts. (Ich habe die Feld-Accessoren und den Konstruktor der Kürze halber entfernt).
Das Coole ist, dass uns das Kotlin-Team mit der Daten
Modifikator für Klassen, um das Schreiben dieser Boilerplate zu vermeiden.
Lassen Sie uns jetzt den vorhergehenden Code in Kotlin schreiben.
Datenklasse BlogPost (Variablentitel: String, Var-URL: URI, Var-Beschreibung: String, Var PublishDate: Date)
Genial! Wir spezifizieren nur die Daten
Modifikator vor dem Klasse
Schlüsselwort, um eine Datenklasse zu erstellen - genau wie bei uns Blogeintrag
Kotlin-Klasse oben. Jetzt die gleich
, Hash-Code
, toString
, Kopieren
, und mehrere komponentenmethoden werden für uns unter der haube erstellt. Beachten Sie, dass eine Datenklasse andere Klassen erweitern kann (dies ist eine neue Funktion von Kotlin 1.1)..
gleich
MethodeDiese Methode vergleicht zwei Objekte auf Gleichheit und gibt true zurück, wenn sie gleich sind oder andernfalls false. Mit anderen Worten, es vergleicht, ob die beiden Klasseninstanzen dieselben Daten enthalten.
student.equals (student3) // mit dem == in Kotlin student == student3 // gleich wie mit equals ()
In Kotlin den Gleichheitsoperator verwenden ==
wird anrufen gleich
Methode hinter den Kulissen.
Hash-Code
Methode Diese Methode gibt einen ganzzahligen Wert zurück, der zum schnellen Speichern und Abrufen von Daten verwendet wird, die in einer Hash-basierten Erfassungsdatenstruktur gespeichert sind, beispielsweise in der HashMap
und HashSet
Sammlungstypen.
toString
MethodeDiese Methode gibt a zurück String
Darstellung eines Objekts.
Datenklasse Person (var Vorname: String, Var Nachname: String) val Person = Person ("Chike", "Mgbemena") println (Person) // druckt "Person (Vorname = Chike, Nachname = Mgbemena)"
Wenn Sie nur die Klasseninstanz aufrufen, erhalten Sie ein String-Objekt, das an us zurückgegeben wird toString ()
unter der haube für uns. Aber wenn wir das nicht setzen Daten
Schlüsselwort in, sehen Sie, wie unsere Objekt-Zeichenfolgendarstellung aussehen würde:
com.chike.kotlin.classes.Person@2f0e140b
Viel weniger informativ!
Kopieren
MethodeDiese Methode ermöglicht es uns, eine neue Instanz eines Objekts mit allen gleichen Eigenschaftswerten zu erstellen. Das heißt, es wird eine Kopie des Objekts erstellt.
val person1 = Person ("Chike", "Mgbemena") println (person1) // Person (firstName = Chike, Nachname = Mgbemena) val person2 = person1.copy () println (person2) // Person (firstName = Chike, lastName) = Mgbemena)
Eine coole Sache über die Kopieren
Methode in Kotlin ist die Fähigkeit, Eigenschaften während des Kopierens zu ändern.
val person3 = person1.copy (lastName = "Onu") println (person3) // Person3 (firstName = Chike, lastName = Onu)
Wenn Sie ein Java-Codierer sind, ähnelt diese Methode der Klon()
Methode, mit der Sie bereits vertraut sind. Aber der Kotlin Kopieren
Methode hat leistungsfähigere Funktionen.
In dem Person
Klasse haben wir auch zwei Methoden, die der Compiler wegen der automatisch für uns generiert hat Daten
Schlüsselwort in der Klasse platziert. Diesen beiden Methoden wird "Komponente" vorangestellt, gefolgt von einem Nummernsuffix: Komponente1 ()
, Komponente2 ()
. Jede dieser Methoden repräsentiert die einzelnen Eigenschaften des Typs. Beachten Sie, dass das Suffix der Reihenfolge der im primären Konstruktor deklarierten Eigenschaften entspricht.
Also in unserem Beispiel aufrufen Komponente1 ()
gibt den Vornamen zurück und ruft an Komponente2 ()
wird den Nachnamen zurückgeben.
println (person3.component1 ()) // Chike println (person3.component2 ()) // Onu
Das Aufrufen der Eigenschaften mit diesem Stil ist schwer zu verstehen und zu lesen. Der explizite Aufruf des Eigenschaftennamens ist jedoch viel besser. Diese implizit erstellten Eigenschaften haben jedoch einen sehr nützlichen Zweck: Sie lassen uns eine Destrukturierungsdeklaration durchführen, in der wir jede Komponente einer lokalen Variablen zuweisen können.
val (Vorname, Nachname) = Person ("Angelina", "Jolie") println (Vorname + "" + Nachname) // Angelina Jolie
Wir haben hier die erste und zweite Eigenschaft direkt zugewiesen (Vorname
und Nachname
) des Person
Geben Sie die Variablen ein Vorname
und Nachname
beziehungsweise. Ich habe auch über diesen Mechanismus gesprochen Zerstörungserklärung im letzten Abschnitt des Beitrags Pakete und Grundfunktionen.
Im Beitrag "Mehr Spaß mit Funktionen" habe ich Ihnen gesagt, dass Kotlin lokale oder geschachtelte Funktionen unterstützt - eine Funktion, die in einer anderen Funktion deklariert ist. Nun, Kotlin unterstützt auf ähnliche Weise auch verschachtelte Klassen - eine Klasse, die in einer anderen Klasse erstellt wurde.
Klasse OuterClass Klasse NestedClass fun nestedClassFunc ()
Wir bezeichnen sogar die öffentlichen Funktionen der verschachtelten Klasse, wenn man sie unter einer verschachtelten Klasse in Kotlin sieht statisch
verschachtelte Klasse in Java. Beachten Sie, dass verschachtelte Klassen keinen Verweis auf ihre äußere Klasse speichern können.
val nestedClass = OuterClass.NestedClass () nestedClass.nestedClassFunc ()
Wir können auch die verschachtelte Klasse als privat festlegen. Dies bedeutet, dass wir nur eine Instanz von erstellen können NestedClass
im Rahmen der OuterClass
.
Innere Klassen hingegen können sich auf die äußere Klasse beziehen, in der sie deklariert wurde. Um eine innere Klasse zu erstellen, platzieren wir die innere
Schlüsselwort vor dem Klasse
Schlüsselwort in einer verschachtelten Klasse.
Klasse OuterClass () val oCPropt: String = "Yo" innere Klasse InnerClass fun innerClassFunc () val outerClass = this @ OuterClass print (outerClass.oCPropt) // druckt "Yo"
Hier verweisen wir auf das OuterClass
von dem InnerClass
durch die Nutzung dieses @ OuterClass
.
Ein Aufzählungstyp deklariert eine Menge von Konstanten, die durch Bezeichner dargestellt werden. Diese spezielle Klasse wird mit dem Schlüsselwort erstellt enum
das ist vor dem angegeben Klasse
Stichwort.
Aufzählungsklasse Land NIGERIA, GHANA, CANADA
Um einen Aufzählungswert basierend auf seinem Namen abzurufen (genau wie in Java), führen wir Folgendes aus:
Länderwert ("NIGERIA")
Oder wir können den Kotlin verwenden enumValueOf
Hilfsmethode zum generischen Zugriff auf Konstanten:
enumValueOf("NIGERIA")
Außerdem können wir alle Werte (wie für ein Java-Enumment) folgendermaßen erhalten:
Länderwerte ()
Schließlich können wir den Kotlin verwenden enumValues
Hilfsmethode, um alle Enum-Einträge auf generische Weise abzurufen:
enumValues()
Dies gibt ein Array zurück, das die Listeneinträge enthält.
Genau wie eine normale Klasse enum
type kann einen eigenen Konstruktor mit Eigenschaften haben, die jeder Enumenkonstante zugeordnet sind.
Aufzählungsklasse Land (val calling code: Int) NIGERIA (234), USA (1), GHANA (233)
In dem Land
Beim primären Konstruktor des Typs "enum" haben wir die unveränderliche Eigenschaft definiert Aufrufcodes
für jede Enumenkonstante. In jeder Konstante haben wir ein Argument an den Konstruktor übergeben.
Wir können dann wie folgt auf die Konstanten-Eigenschaft zugreifen:
val country = country.NIGERIA print (country.callingCode) // 234
Eine versiegelte Klasse in Kotlin ist eine abstrakte Klasse (Sie möchten niemals Objekte daraus erstellen), die andere Klassen erweitern können. Diese Unterklassen werden innerhalb des versiegelten Klassenkörpers in derselben Datei definiert. Da alle diese Unterklassen innerhalb des versiegelten Klassentexts definiert sind, können wir alle möglichen Unterklassen kennen, indem Sie einfach die Datei anzeigen.
Sehen wir uns ein praktisches Beispiel an.
// Shape.kt Sealed-Klasse Shape-Klasse Circle: Shape () - Klasse Dreieck: Shape () - Klasse Rechteck: Shape ()
Um eine Klasse als versiegelt zu deklarieren, setzen wir die versiegelt
Modifikator vor dem Klasse
Modifikator im Header der Klassendeklaration. In unserem Fall haben wir das deklariert Gestalten
Klasse als versiegelt
. Eine versiegelte Klasse ist ohne ihre Unterklassen - wie eine typische abstrakte Klasse - unvollständig. Daher müssen wir die einzelnen Unterklassen in derselben Datei deklarieren (shape.kt in diesem Fall). Beachten Sie, dass Sie keine Unterklasse einer versiegelten Klasse aus einer anderen Datei definieren können.
In unserem Code oben haben wir angegeben, dass Gestalten
Klasse kann nur von den Klassen erweitert werden Kreis
, Dreieck
, und Rechteck
.
Sealed-Klassen in Kotlin haben die folgenden zusätzlichen Regeln:
abstrakt
zu einer versiegelten Klasse, aber dies ist überflüssig, da versiegelte Klassen standardmäßig abstrakt sind.öffnen
oder Finale
Modifikator. Klassen, die Unterklassen einer versiegelten Klasse erweitern, können entweder in derselben Datei oder in einer anderen Datei abgelegt werden. Die versiegelte Klassenunterklasse muss mit gekennzeichnet werden öffnen
Modifikator (im nächsten Beitrag erfahren Sie mehr über die Vererbung in Kotlin).
// Employee.kt Sealed Class Mitarbeiter offene Klasse Artist: Employee () // Musician.kt Class Musiker: Artist ()
Eine versiegelte Klasse und ihre Unterklassen sind in einer sehr praktisch wann
Ausdruck. Zum Beispiel:
fun whatIsIt (Shape: Shape) = Wenn (Shape) Kreis -> Println ("Ein Kreis") ist Triangle -> Println ("Ein Dreieck") ist Rechteck -> Println ("Ein Rechteck")
Hier ist der Compiler intelligent, um sicherzustellen, dass wir alles Mögliche abgedeckt haben wann
Fälle. Das bedeutet, dass Sie keine hinzufügen müssen sonst
Klausel.
Wenn wir stattdessen Folgendes tun würden:
fun whatIsIt (shape: Shape) = wann (shape) ist Circle -> println ("A circle") ist Triangle -> println ("A triangle")
Der Code konnte nicht kompiliert werden, da nicht alle möglichen Fälle berücksichtigt wurden. Wir hätten den folgenden Fehler:
Kotlin: "Wenn" der Ausdruck muss erschöpfend sein, fügen Sie den erforderlichen Wert hinzu "ist der Zweig" Rechteck "oder der Zweig" else ".
Also könnten wir entweder das aufnehmen ist Rechteck
Fall oder umfassen die sonst
Klausel zur Vollendung der wann
Ausdruck.
In diesem Tutorial haben Sie mehr über den Unterricht in Kotlin erfahren. Wir haben die folgenden Klasseneigenschaften behandelt:
Außerdem haben Sie einige coole und fortgeschrittene Klassen wie Daten, Enumeration, verschachtelte und versiegelte Klassen kennengelernt. Im nächsten Tutorial der Kotlin From Scratch-Serie werden Schnittstellen und Vererbung in Kotlin vorgestellt. Bis bald!
Um mehr über die Kotlin-Sprache zu lernen, empfehle ich die Kotlin-Dokumentation. Auf Envato Tuts finden Sie auch einige unserer anderen Android-Apps zur Entwicklung von Apps!