Gids voor Kotlin-interfaces

1. Overzicht

In deze tutorial we zullen bespreken hoe interfaces in Kotlin kunnen worden gedefinieerd en geïmplementeerd.

We zullen ook bekijken hoe meerdere interfaces door een klas kunnen worden geïmplementeerd. Dit kan zeker conflicten veroorzaken, en we zullen het mechanisme leren dat Kotlin heeft om ze op te lossen.

2. Interfaces in Kotlin

Een interface is een manier om een ​​beschrijving of contract te geven voor klassen in objectgeoriënteerd programmeren. Ze kunnen eigenschappen en functies op abstracte of concrete manieren bevatten, afhankelijk van de programmeertaal. We zullen de details van interfaces in Kotlin doornemen.

Interfaces in Kotlin zijn vergelijkbaar met interfaces in veel andere talen, zoals Java. Maar ze hebben een specifieke syntaxis, laten we ze in de volgende subsecties bekijken.

2.1. Interfaces definiëren

Laten we beginnen met het definiëren van onze eerste interface in Kotlin:

interface SimpleInterface

Dit is de eenvoudigste interface die helemaal leeg is. Deze worden ook wel markeringsinterfaces genoemd.

Laten we nu enkele functies aan onze interface toevoegen:

interface SimpleInterface {fun firstMethod (): String fun secondMethod (): String {return ("Hello, World!")}}

We hebben twee methoden toegevoegd aan onze eerder gedefinieerde interface:

  • Een van hen belde firstMethod is een abstracte methode
  • Terwijl de andere second Methode heeft een standaardimplementatie.

Laten we doorgaan en nu enkele eigenschappen aan onze interface toevoegen:

interface SimpleInterface {val firstProp: String val secondProp: String get () = "Tweede eigenschap" fun firstMethod (): String fun secondMethod (): String {return ("Hallo, van:" + secondProp)}}

Hier hebben we twee eigenschappen aan onze interface toegevoegd:

  • Een van hen belde firstProp is van het type String en is abstract
  • De tweede belde secondProp is ook van het type string, maar het definieert een implementatie voor zijn accessor.

Let daar op eigenschappen in een interface kunnen de status niet behouden. Het volgende is dus een illegale uitdrukking in Kotlin:

interface SimpleInterface {val firstProp: String = "First Property" // Illegale declaratie}

2.2. Interfaces implementeren

Nu we een basisinterface hebben gedefinieerd, laten we eens kijken hoe we dat kunnen implementeren in een klas in Kotlin:

class SimpleClass: SimpleInterface {overschrijven val firstProp: String = "Eerste eigenschap" fun firstMethod (): String {return ("Hello, from:" + firstProp)}}

Merk op dat wanneer we definiëren SimpleClass als een implementatie van SimpleInterface, we hoeven alleen de implementatie te voorzien voor abstracte eigenschappen en functies. Echter, wij kan ook elke eerder gedefinieerde eigenschap of functie overschrijven.

Laten we nu alle eerder gedefinieerde eigenschappen en functies in onze klasse negeren:

class SimpleClass: SimpleInterface {overschrijf val firstProp: String = "Eerste eigenschap" overschrijf val secondProp: String get () = "Tweede eigenschap, overschreven!" override fun firstMethod (): String {return ("Hello, from:" + firstProp)} override fun secondMethod (): String {return ("Hello, from:" + secondProp + firstProp)}}

Hier hebben we het eigendom overschreven secondProp en de functie secondFunction die eerder waren gedefinieerd in de interface SimpleInterface.

2.3 Interfaces implementeren via delegatie

Delegatie is een ontwerppatroon bij objectgeoriënteerd programmeren herbruikbaarheid van code te bereiken door middel van compositie in plaats van overerving. Hoewel dit in veel talen kan worden geïmplementeerd, zoals Java, Kotlin heeft native ondersteuning voor implementatie door middel van delegatie.

Als we beginnen met een basisinterface en klasse:

interface MyInterface {fun someMethod (): String} class MyClass (): MyInterface {override fun someMethod (): String {return ("Hello, World!")}}

Tot nu toe niets nieuws. Maar nu kunnen we een andere klasse definiëren die implementeert MyInterface via delegatie:

klasse MyDerivedClass (myInterface: MyInterface): MyInterface door myInterface

MyDerivedClass verwacht een afgevaardigde als argument dat de interface daadwerkelijk implementeert MyInterface.

Laten we eens kijken hoe we een functie van de interface kunnen aanroepen door middel van delegeren:

val myClass = MyClass () MyDerivedClass (myClass) .someMethod ()

Hier hebben we geïnstantieerd Mijn klas en gebruikte dat als de afgevaardigde om functies van de interface aan te roepen MyDerivedClass, die deze functies eigenlijk nooit rechtstreeks hebben geïmplementeerd.

3. Meerdere overerving

Meervoudige overerving is een sleutelbegrip in het objectgeoriënteerde programmeerparadigma. Hierdoor kan een klasse kenmerken erven van meer dan één bovenliggend object, zoals bijvoorbeeld een interface.

Hoewel dit meer flexibiliteit biedt bij het modelleren van objecten, heeft het zijn eigen complexiteit. Een daarvan is het "diamantprobleem".

Java 8 heeft zijn eigen mechanismen om het diamantprobleem aan te pakken, net als elke andere taal die meervoudige overerving mogelijk maakt.

Laten we eens kijken hoe Kotlin het aanpakt via interfaces.

3.1. Meerdere interfaces overnemen

We beginnen met het definiëren van twee eenvoudige interfaces:

interface FirstInterface {fun someMethod (): String fun anotherMethod (): String {return ("Hallo, van anotherMethod in FirstInterface")}} interface SecondInterface {fun someMethod (): String {return ("Hallo, van someMethod in SecondInterface") } fun anotherMethod (): String {return ("Hallo, van anotherMethod in SecondInterface")}}

Merk op dat beide interfaces methoden hebben met hetzelfde contract.

Laten we nu een klasse definiëren die erft van beide interfaces:

class SomeClass: FirstInterface, SecondInterface {override fun someMethod (): String {return ("Hello, from someMethod in SomeClass")} override fun anotherMethod (): String {return ("Hallo, van anotherMethod in SomeClass")}}

Zoals we kunnen zien, SomeClass implementeert beide Eerste interface en SecondInterface. Hoewel dit syntactisch vrij eenvoudig is, is er een beetje semantiek dat hier aandacht vereist. We zullen dit in de volgende onderafdeling bespreken.

3.2. Conflicten oplossen

Bij het implementeren van meerdere interfaces kan een klasse een functie erven die een standaardimplementatie heeft voor hetzelfde contract in meerdere interfaces. Dit roept het probleem op van het aanroepen van deze functie vanuit een instantie van de uitvoerende klasse.

Om dit conflict op te lossen, vereist Kotlin dat de subklasse een overschreven implementatie biedt voor dergelijke functies om de oplossing expliciet te maken.

Bijvoorbeeld, SomeClass bovenstaande werktuigen een andere methode. Maar als dat niet zo was, zou Kotlin niet weten of hij een beroep zou moeten doen Eerste of SecondInterface's standaard implementatie van een andere methode. SomeClass moet implementeren een andere methode om deze reden.

Echter, eenMethode is een beetje anders omdat er eigenlijk geen conflict is. Eerste interface biedt geen standaardimplementatie voor eenMethode. Dat gezegd hebbende, SomeClass moet het nog steeds implementeren omdat Kotlin dwingt ons om alle overgeërfde functies te implementeren, ongeacht of ze een of meerdere keren zijn gedefinieerd in bovenliggende interfaces.

3.3. Het diamantprobleem oplossen

Een "diamantprobleem" treedt op wanneer twee onderliggende objecten van een basisobject een bepaald gedrag beschrijven dat wordt gedefinieerd door het basisobject. Nu moet een object dat van beide onderliggende objecten erft, oplossen op welk erfelijk gedrag het zich abonneert.

Kotlin's oplossing voor dit probleem is door middel van de regels die zijn gedefinieerd voor meervoudige overerving in de vorige paragraaf. Laten we een paar interfaces en een implementatieklasse definiëren om het diamantprobleem te presenteren:

interface BaseInterface {fun someMethod (): String} interface FirstChildInterface: BaseInterface {override fun someMethod (): String {return ("Hallo, van someMethod in FirstChildInterface")}} interface SecondChildInterface: BaseInterface {override fun someMethod (): String {return ("Hallo, van someMethod in SecondChildInterface")}} class ChildClass: FirstChildInterface, SecondChildInterface {override fun someMethod (): String {return super.someMethod ()}}

Hier hebben we gedefinieerd BaseInterface die een abstracte functie verklaarde genaamd eenMethode. Beide interfaces FirstChildInterface en SecondChildInterface erft van BaseInterface en implementeer de functie eenMethode.

Nu zoals we implementeren ChildClass erven van FirstChildInterface en SecondChildInterface, is het noodzakelijk dat we de functie overschrijven eenMethode. Echter, ook al moeten we de methode overschrijven, we kunnen nog steeds gewoon bellen super net zo we doen hier met SecondChildInterface.

4. Interfaces vergeleken met abstracte klassen in Kotlin

Abstracte lessen in Kotlin zijn klassen die niet kunnen worden geïnstantieerd. Dit kan een of meer eigenschappen en functies bevatten. Deze eigenschappen en functies kunnen abstract of concreet zijn. Elke klasse die van een abstracte klasse erft, moet alle overgeërfde abstracte eigenschappen en functies implementeren, tenzij die klasse zelf ook als abstract wordt gedeclareerd.

4.1. Verschillen tussen interface en abstracte klasse

Wacht! Klinkt dat niet precies zoals een interface doet?

In feite verschilt een abstracte klasse in het begin niet veel van de interface. Maar er zijn subtiele verschillen die bepalend zijn voor de keuze die we maken:

  • Een klasse in Kotlin kan zoveel interfaces implementeren als ze willen, maar het kan zich alleen uitstrekken van één abstracte klasse
  • Eigenschappen in de interface kunnen de status niet behouden, terwijl ze dat wel kunnen in een abstracte klasse

4.2. Wanneer moeten we wat gebruiken?

Een interface is slechts een blauwdruk voor het definiëren van klassen, ze kunnen optioneel ook enkele standaardimplementaties hebben. Aan de andere kant is een abstracte klasse een onvolledige implementatie die wordt voltooid door de uitbreidende klassen.

Typisch interfaces moeten worden gebruikt om het contract te definiëren, wat de mogelijkheden ontlokt die het belooft te leveren. Een uitvoerende klasse heeft de verantwoordelijkheid om die beloften na te komen. Een abstracte klasse moet echter worden gebruikt om partiële kenmerken te delen met uitbreidende klassen. Een uitbreidende klasse kan er verder mee bezig zijn om deze te voltooien.

5. Vergelijking met Java-interfaces

Met de wijzigingen in de Java-interface in Java 8, ze zijn heel dicht bij Kotlin-interfaces gekomen. Een van onze vorige artikelen bevat de nieuwe functies die in Java 8 zijn geïntroduceerd, inclusief wijzigingen in de interface.

Er zijn nu vooral syntactische verschillen tussen Java- en Kotlin-interfaces. Een verschil dat opvalt, is gerelateerd aan het trefwoord "overschrijven". In Kotlin is het tijdens het implementeren van abstracte eigenschappen of functies die van een interface zijn geërfd, verplicht om ze te kwalificeren met het trefwoord "overschrijven“. Een dergelijke expliciete vereiste bestaat niet in Java.

6. Conclusie

In deze tutorial hebben we Kotlin-interfaces besproken, hoe deze te definiëren en te implementeren. Toen hadden we het over het erven van meerdere interfaces en het conflict dat ze kunnen veroorzaken. We hebben gekeken hoe Kotlin met dergelijke conflicten omgaat.

Ten slotte hebben we interfaces besproken in vergelijking met abstracte klassen in Kotlin. We hebben ook kort gesproken over hoe de Kotlin-interface zich verhoudt tot de Java-interface.

Zoals altijd is de code voor de voorbeelden beschikbaar op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found