Kotlin from Scratch abstracte klassen, interfaces, overerving en typealias

Kotlin is een moderne programmeertaal die compileert met Java bytecode. Het is gratis en open source en belooft om het coderen voor Android nog leuker te maken.  

In het vorige artikel hebt u meer informatie gekregen over Kotlin-eigenschappen zoals late-initialisatie, extensie en inline-eigenschappen. Niet alleen dat, je hebt ook geleerd over geavanceerde klassen zoals data, enum, geneste en verzegelde klassen in Kotlin. 

In dit bericht blijf je leren over objectgeoriënteerd programmeren in Kotlin door te leren over abstracte klassen, interfaces en overerving. Voor een bonus leer je ook over type aliassen. 

1. Abstracte klassen

Kotlin ondersteunt abstracte klassen - net als Java zijn dit klassen waarvan je nooit van plan bent objecten te maken. Een abstracte klasse is onvolledig of nutteloos zonder een aantal concrete (niet-abstracte) subklassen, waaruit u objecten kunt instantiëren. Een concrete subklasse van een abstracte klasse implementeert alle methoden en eigenschappen gedefinieerd in de abstracte klasse, anders is die subklasse ook een abstracte klasse!

We creëren een abstracte klasse met de abstract modifier (vergelijkbaar met Java). 

abstracte klasse Werknemer (val firstName: String, val lastName: String) abstract fun earnings (): Double

Merk op dat niet alle leden abstract moeten zijn. Met andere woorden, we kunnen methode-standaardimplementatie in een abstracte klasse hebben. 

abstracte klasse Werknemer (val firstName: String, val lastName: String) // ... fun fullName (): String return lastName + "" + firstName; 

Hier hebben we de niet-abstracte functie gemaakt voor-en achternaam() in een abstracte klasse werknemer. Betonklassen (subklassen van de abstracte klasse) kunnen de standaardimplementatie van een abstracte methode overschrijven, maar alleen als de methode de Open modifier opgegeven (hier komt u binnenkort meer over te weten). 

We kunnen ook staat opslaan in abstracte klassen. 

abstracte klasse Werknemer (val firstName: String, val lastName: String) // ... val propFoo: String = "bla bla"

Zelfs als de abstracte klasse geen methoden definieert, moeten we een subklasse maken voordat we deze kunnen instantiëren, zoals in het onderstaande voorbeeld.

class Programmer (firstName: String, lastName: String): Employee (firstName, lastName) overschrijft leuke inkomsten (): verdubbelen // inkomsten berekenen

Onze Programmeur class breidt het uit werknemer abstracte klasse. In Kotlin gebruiken we een enkel dubbel teken (:) in plaats van Java strekt sleutelwoord om een ​​klasse uit te breiden of een interface te implementeren. 

We kunnen vervolgens een object van het type maken Programmeur en bel methoden erop - hetzij in zijn eigen klasse of de superklasse (basisklasse).  

val programmeur = Programmeur ("Chike", "Mgbemena") println (programmer.fullName ()) // "Mgbemena Chike"

Een ding dat je zou kunnen verbazen is dat we de mogelijkheid hebben om een ​​te vervangen val (onveranderlijk) eigendom met var (veranderlijk). 

open class BaseA (open val baseProp: String)  class DerivedA: BaseA ("") private var derivedProp: String = "" negeer var baseProp: String get () = derivedProp set (waarde) derivedProp = value

Zorg ervoor dat u deze functionaliteit verstandig gebruikt! Houd er rekening mee dat we de omgekeerde a niet kunnen doen var woning met val

2. interfaces

Een interface is eenvoudigweg een verzameling gerelateerde methoden waarmee u standaard objecten kunt vertellen wat u moet doen en ook hoe u dit standaard moet doen. (Standaardmethoden in interfaces zijn een nieuwe functie toegevoegd aan Java 8.) Met andere woorden, een interface is een contract dat door klassen wordt geïmplementeerd.. 

Een interface wordt gedefinieerd met behulp van de interface trefwoord in Kotlin (vergelijkbaar met Java). 

class Result class Student interface StudentRepository fun getById (id: Long): Student fun getResultsById (id: Long): Lijst 

In de bovenstaande code hebben we een verklaard StudentRepository interface. Deze interface bevat twee abstracte methoden: getById () en getResultsById (). Merk op dat de abstract trefwoord is overbodig in een interfacemethode omdat ze al impliciet abstract zijn. 

Een interface is nutteloos zonder een of meer implementeerders, dus laten we een klasse maken die deze interface implementeert. 

class StudentLocalDataSource: StudentRepository overschrijven leuk getResults (id: lang): lijst // do implementation overschrijven leuk getById (id: lang): student // do implementation

Hier hebben we een klas gemaakt StudentLocalDataSource dat implementeert de StudentRepository interface.

Wij gebruiken de override modifier om de methoden en eigenschappen te labelen die we opnieuw willen definiëren vanuit de interface of superklasse, dit is vergelijkbaar met de @ Override annotatie in Java.

Let op de volgende aanvullende regels van interfaces in Kotlin:

  • Een klasse kan zoveel interfaces implementeren als je wilt, maar het kan maar één klasse uitbreiden (vergelijkbaar met Java).
  • De override modifier is verplicht in Kotlin-in tegenstelling tot Java. 
  • Naast methoden kunnen we eigenschappen ook in een Kotlin-interface declareren. 
  • Een Kotlin-interfacemethode kan een standaardimplementatie hebben (vergelijkbaar met Java 8). 

Laten we een voorbeeld van een interfacemethode bekijken met een standaardimplementatie.

interface StudentRepository // ... fun delete (student: student) // do implementatie

In de voorgaande code hebben we een nieuwe methode toegevoegd wissen () met een standaardimplementatie (hoewel ik de daadwerkelijke implementatiecode niet heb toegevoegd voor demonstratiedoeleinden). 

We hebben ook de vrijheid om de standaardimplementatie te overschrijven als we dat willen.

klas StudentLocalDataSource: StudentRepository // ... overschrijven leuke verwijdering (student: student) // do implementatie

Zoals gezegd, kan een Kotlin-interface eigenschappen hebben, maar merk op dat deze de status niet kan behouden. (Onthoud echter dat abstracte klassen de status kunnen behouden.) Dus de volgende interfacedefinitie met een eigenschapverklaring werkt.

interface StudentRepository val propFoo: Boolean // zal werken // ...

Maar als we proberen een of andere status toe te voegen aan de interface door een waarde toe te kennen aan de eigenschap, zal deze niet werken.

interface StudentRepository val propFoo: Boolean = true // Fout: eigenschap-initializers zijn niet toegestaan ​​in interfaces // ...

Een interface-eigenschap in Kotlin kan echter getter- en settermethoden hebben (hoewel alleen de laatste als de eigenschap veranderbaar is). Merk ook op dat de eigenschap in een interface geen back-veld kan hebben. 

interface StudentRepository var propFoo: Boolean get () = true set (value) if (value) // do something // ...

We kunnen ook een interface-eigenschap desgewenst overschrijven om deze opnieuw te definiëren. 

class StudentLocalDataSource: StudentRepository // ... negeer var propFoo: Boolean get () = false set (value) if (value) 

Laten we eens kijken naar een geval waarin we een klasse hebben die meerdere interfaces implementeert met dezelfde methodesignatuur. Hoe beslist de klas welke interfacemethode te bellen?

interface InterfaceA fun funD ()  interface InterfaceB fun funD () 

Hier hebben we twee interfaces met een methode met dezelfde handtekening fonds(). Laten we een klasse maken die deze twee interfaces implementeert en de. Overschrijft fonds() methode. 

class classA: InterfaceA, InterfaceB override fun funD () super.funD () // Fout: er zijn veel supertypes beschikbaar, geef dan aan welke u bedoelt onder punthaken, bijv. 'super'

De compiler is verward over het bellen van de super.funD () methode omdat de twee interfaces die de klasse implementeert dezelfde methodesignatuur hebben. 

Om dit probleem op te lossen, verpakken we de interfacenaam waarvoor we de methode tussen punthaken willen noemen . (IntelliJ IDEA of Android Studio geeft u een hint over het oplossen van dit probleem wanneer het opduikt.)

class classA: InterfaceA, InterfaceB override fun funD () super.funD ()

Hier gaan we de fonds()  methode van InterfaceA. Probleem opgelost! 

3. Overerving

Een nieuwe klasse (subklasse) wordt gemaakt door een bestaande klasse (superklasse) leden te verwerven en misschien hun standaardimplementatie opnieuw te definiëren. Dit mechanisme staat bekend als erfenis in object-georiënteerd programmeren (OOP). Een van de dingen die Kotlin zo geweldig maken, is dat het zowel de OOP- als de functionele programmeerparadigma omvat - alles in één taal.

De basisklasse voor alle klassen in Kotlin is Ieder

class Person: Any 

De Ieder type is gelijk aan de Voorwerp type dat we in Java hebben. 

openbare open klasse Any public open operator fun equals (other: Any?): Boolean public open fun hashCode (): Int public open fun toString (): String

De Ieder type bevat de volgende leden: is gelijk aan (), Hashcode (), en ook toString () methoden (vergelijkbaar met Java). 

Onze klassen hoeven dit type niet expliciet uit te breiden. Als u niet expliciet opgeeft in welke klasse een nieuwe klasse zich uitbreidt, wordt de klasse uitgebreid Ieder impliciet. Om deze reden hoeft u dit meestal niet op te nemen : Ieder in uw code - we doen dit in de bovenstaande code voor demonstratiedoeleinden. 

 Laten we nu kijken naar het creëren van lessen in Kotlin met het oog op erfgoed. 

klas Student  klas GraduateStudent: Student () 

In de bovenstaande code, de GraduateStudent class breidt de superklasse uit Student. Maar deze code compileert niet. Waarom? Omdat klassen en methoden zijn laatste standaard in Kotlin. Met andere woorden, ze kunnen niet standaard worden uitgebreid, in tegenstelling tot Java, waar klassen en methoden standaard zijn geopend. 

Aanbevolen procedures voor softwaretechnologie bevelen aan dat u begint met het maken van uw klassen en methoden laatste standaard - d.w.z. als ze niet specifiek zijn bedoeld om opnieuw te worden gedefinieerd of te worden onderdrukt in subklassen. Het Kotlin-team (JetBrains) paste deze codeerfilosofie en nog veel meer praktische ontwikkelingspraktijken toe bij het ontwikkelen van deze moderne taal. 

Om subklassen van een superklasse te kunnen maken, moeten we de superklasse expliciet markeren met de Open modifier. Deze wijziging is ook van toepassing op elke superklasse-eigenschap of -methode die moet worden overschreven door subklassen.

open klasse Student 

We plaatsen gewoon de Open modifier voor de klasse trefwoord. We hebben nu de compiler opdracht gegeven om onze Student klasse om open te staan ​​voor extensie. 

Zoals eerder vermeld, zijn leden van een Kotlin-klasse standaard ook definitief. 

open klasse Student open leuk schoolgeld (): BigDecimal // do implementatie

In de voorgaande code hebben we de schoolgeld Functioneert als Open-zodat subklassen het kunnen negeren. 

open class GraduateStudent: Student () override fun schoolFees (): BigDecimal return super.schoolFees () + calculationSchoolFees () private fun calculateSchoolFees (): BigDecimal // schoolkosten berekenen en inleveren

Hier, de open schoolgeld functie van de superklasse Student wordt overschreven door de GraduateStudent klasse - door de override modifier voor de pret trefwoord. Merk op dat als u een lid van een superklasse of interface opheft, het overheersende lid dat ook is Open standaard, zoals in het onderstaande voorbeeld:

class ComputerScienceStudent: GraduateStudent () override fun schoolFees (): BigDecimal retourneer super.schoolFees () + calculationSchoolFess () private fun calculateSchoolFess (): BigDecimal // bereken en retourneer schoolgeld

Hoewel we het niet hebben gemarkeerd Schoolgeld () methode in de GraduateStudent klasse met de Open modifier, we kunnen het nog steeds negeren, zoals we deden in de ComputerScienceStudent klasse. Om dit te voorkomen, moeten we het overheersende lid markeren als laatste

Vergeet niet dat we nieuwe functionaliteit aan een klasse kunnen toevoegen, zelfs als die definitief is, door het gebruik van uitbreidingsfuncties in Kotlin. Voor een opfriscursus over uitbreidingsfuncties, bekijk mijn geavanceerde functies in Kotlin-bericht. Als je een opfriscursus nodig hebt over hoe je zelfs een laatste klas nieuwe eigenschappen kunt geven zonder ervan te erven, lees dan het gedeelte over de extensie Eigenschappen in mijn Geavanceerde eigenschappen en klassen. 

Als onze superklasse een primaire constructor als deze heeft:

open klasse Student (val firstName: String, val lastName: String) // ...

Vervolgens moet elke subklasse de primaire constructor van de superklasse aanroepen. 

open class GraduateStudent (firstName: String, lastName: String): Student (firstName, lastName) // ...

We kunnen eenvoudig een object van de maken GraduateStudent klasse zoals gebruikelijk:

val graduateStudent = GraduateStudent ("Jon", "Snow") println (graduateStudent.firstName) // Jon

Als de subklasse de superclass-constructor van de secundaire constructor wil aanroepen, gebruiken we de super keyword (vergelijkbaar met hoe superclass constructors worden aangeroepen in Java). 

open class GraduateStudent: Student // ... private var thesis: String = "" constructor (firstName: String, lastName: String, thesis: String): super (firstName, lastName) this.thesis = thesis

Als je opfriscursussen nodig hebt bij klasconstructeurs in Kotlin, bezoek dan alstublieft mijn Classes and Objects-post. 

4. Bonus: type alias

Een ander geweldig ding dat we kunnen doen in Kotlin is om een ​​type een alias te geven. 

Laten we een voorbeeld bekijken.

data class Person (val firstName: String, val lastName: String, val age: Int)

In de bovenstaande klas kunnen we de Draad en Int typen voor de Persoon eigenschappen aliassen met behulp van de typealias modifier in Kotlin. Deze modifier wordt gebruikt om een ​​alias van elk type in Kotlin te maken, inclusief degenen die je hebt gemaakt. 

typealias Name = String typealias Age = Int data class Person (val firstName: Name, val lastName: Name, val age: Age) 

Zoals u kunt zien, hebben we een alias gemaakt Naam en Leeftijd voor zowel de Draad en Int typen respectievelijk. We hebben nu de Voornaam en achternaam eigenschaptype voor onze alias Naam-en ook Int type naar Leeftijd alias. Merk op dat we geen nieuwe typen hebben gemaakt - in plaats daarvan hebben we een alias voor de typen gemaakt. 

Deze kunnen handig zijn als u een betere betekenis of semantiek wilt geven aan typen in uw Kotlin-codebase. Gebruik ze dus verstandig! 

Conclusie

In deze tutorial leer je meer over objectgeoriënteerd programmeren in Kotlin. We hebben het volgende behandeld:

  • abstracte klassen
  • interfaces 
  • erfenis
  • type alias

Als je Kotlin hebt leren kennen via onze Kotlin From Scratch-serie, zorg er dan voor dat je de code die je ziet hebt ingevoerd en deze op je IDE hebt uitgevoerd. Een goede tip om echt een nieuwe programmeertaal (of een ander programmeerconcept) te begrijpen, is om ervoor te zorgen dat u niet alleen de leermiddelen of handleiding leest, maar ook de eigenlijke code typt en uitvoert!

In de volgende zelfstudie in de Kotlin From Scratch-serie maakt u kennis met de afhandeling van uitzonderingen in Kotlin. Tot ziens!

Voor meer informatie over de Kotlin-taal, raad ik aan de Kotlin-documentatie te bezoeken. Of bekijk enkele van onze andere Android-apps voor app-ontwikkeling hier op Envato Tuts!