Kotlin von Grund auf Erweiterte Eigenschaften und Klassen

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:

  • spät initialisierte Eigenschaften
  • Inline-Eigenschaften 
  • Erweiterungseigenschaften
  • Daten, Enumeration, verschachtelte und versiegelte Klassen

1. Spätinitialisierte Eigenschaften

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:

  • Es muss veränderlich sein (deklariert mit var).
  • Der Eigenschaftstyp kann beispielsweise kein primitiver Typ sein, Int, Doppelt, Schweben, und so weiter. 
  • Die Eigenschaft kann keinen benutzerdefinierten Getter oder Setter haben.

2. Inline-Eigenschaften

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

3. Erweiterungseigenschaften 

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. 

4. Datenklassen

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-Codegleich, 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 gleichHash-CodetoStringKopieren, 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).. 

Das gleich Methode

Diese 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.

Das 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.  

Das toString Methode

Diese 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!

Das Kopieren Methode

Diese 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. 

Zerstörerische Erklärung

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. 

5. Verschachtelte Klassen

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 Klasse

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.

6. Aufzählungsklassen

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.  

Enum-Konstruktoren

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

7. Versiegelte Klassen

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 KreisDreieck, und Rechteck.

Sealed-Klassen in Kotlin haben die folgenden zusätzlichen Regeln:

  • Wir können den Modifikator hinzufügen abstrakt zu einer versiegelten Klasse, aber dies ist überflüssig, da versiegelte Klassen standardmäßig abstrakt sind.
  • Versiegelte Klassen können das nicht haben öffnen oder Finale Modifikator. 
  • Wir können auch Datenklassen und Objekte zu einer versiegelten Klasse als Unterklassen deklarieren (sie müssen noch in derselben Datei deklariert werden).. 
  • Versiegelte Klassen dürfen keine öffentlichen Konstruktoren haben - ihre Konstruktoren sind standardmäßig privat. 

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. 

Fazit

In diesem Tutorial haben Sie mehr über den Unterricht in Kotlin erfahren. Wir haben die folgenden Klasseneigenschaften behandelt:

  • späte Initialisierung
  • Inline-Eigenschaften 
  • Erweiterungseigenschaften

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!