Gebruik van standaard functie-implementatie van interface in Kotlin

Ik heb een Kotlin-interface met een standaardimplementatie, bijvoorbeeld:

interface Foo {
    fun bar(): String {
        return "baz"
    }
}

Dit zou in orde zijn totdat ik deze interface vanuit Java probeer te implementeren. Als ik dat doe, staat er dat de klasse als abstract moet worden gemarkeerd of de methode bar()moet implementeren. Ook wanneer ik de methode probeer te implementeren, kan ik super.bar()niet aanroepen.


Antwoord 1, autoriteit 100%

Het genereren van echte default-methoden die vanuit Java kunnen worden aangeroepen, is een experimentele-functie van Kotlin 1.2.40.

Je moet de methoden annoteren met de @JvmDefaultannotatie:

interface Foo {
    @JvmDefault
    fun bar(): String {
        return "baz"
    }
}

Deze functie is nog steeds standaard uitgeschakeld, je moet de vlag -Xjvm-default=enabledoorgeven aan de compiler om te kunnen werken. (Als je dit in Gradle moet doen, kijk dan hier).

Het is echter echt experimenteel. De blogpost waarschuwt dat zowel het ontwerp als de implementatie in de toekomst kunnen veranderen, en in mijn IDE worden Java-klassen nog steeds gemarkeerd met fouten omdat ze deze methoden niet implementeren, ondanks het compileren en goed werken.


Antwoord 2, autoriteit 56%

Bekijk het gerelateerde probleem.

Er staat een aanbeveling in de opmerkingen:

Schrijf uw interface in Java (met standaardmethoden) en zowel de Java- als Kotlin-klassen gebruiken deze standaardwaarden op de juiste manier


Antwoord 3, autoriteit 31%

Als u weet dat u de functie in geen enkele implementatie van uw interface zult overschrijven,kunt u extensiefunctiesgebruiken als een goede oplossing voor dit probleem. Plaats gewoon een extensiefunctie in hetzelfde bestand als de interface (en op het hoogste niveau zodat andere bestanden het kunnen gebruiken).

Wat u doet kan bijvoorbeeld op deze manier worden gedaan:

interface Foo {
    // presumably other stuff
}
fun Foo.bar(): String {
    return "baz"
}

Zie de documenten over extensiefunctiesvoor meer informatie hierover.

Eén punt dat het vermelden waard is:

We willen benadrukken dat uitbreidingsfuncties statisch worden verzonden, d.w.z. ze zijn niet virtueel per type ontvanger. Dit betekent dat de aangeroepen extensiefunctie wordt bepaald door het type expressie waarop de functie wordt aangeroepen, niet door het type resultaat van het evalueren van die expressie tijdens runtime.

Simpel gezegd, extensiefuncties doen niet wat je zou verwachten van regulier polymorfisme.Dit betekent voor deze tijdelijke oplossing dat de standaardfunctie niet kan worden overschreven zoals een normale functie. Als je het probeert te negeren, krijg je raar gedrag, omdat de “overschreven” versie wordt aangeroepen wanneer je expliciet met de subklasse te maken hebt, maar de extensieversie wordt aangeroepen als je in het algemeen met de interface te maken hebt . Bijvoorbeeld:

interface MyInterface {
    fun a()
}
fun MyInterface.b() {
    println("MyInterface.b() default implementation")
}
class MyInterfaceImpl : MyInterface {
    override fun a() {
        println("MyInterfaceImpl.a()")
    }
    fun b() {
        println("MyInterfaceImpl.b() \"overridden\" implementation")
    }
}
fun main(args: Array<String>) {
    val inst1: MyInterface = MyInterfaceImpl()
    inst1.a()
    inst1.b() // calls the "default" implementation
    val inst2: MyInterfaceImpl = MyInterfaceImpl() // could also just do "val inst2 = MyInterfaceImpl()" (the type is inferred)
    inst2.a()
    inst2.b() // calls the "overridden" implementation
}

Antwoord 4

In tegenstelling tot eerdere versies van Java8, kan Kotlin een standaardimplementatie hebben in de interface.

Wanneer u de Foo-interface implementeert in een Java-klasse. Kotlin verbergt die implementatie van de interface-methode. Zoals hiervermeld.

Arrays worden gebruikt met primitieve datatypes op het Java-platform om de kosten van in- en uitpakken te vermijden. Omdat Kotlin die implementatiedetails verbergt, is er een tijdelijke oplossing nodig om te communiceren met Java-code

Dit is specifiek voor arrays in bovenstaande link, maar het is ook van toepassing op alle klassen (mogelijk om ondersteuning te bieden voor eerdere versies van Java8).

BEWERKEN

Bovenstaande uitleg is gebaseerd op meningen.

Eén ding kwam ik tegen en dat is de belangrijkste reden.

Kotlin-binaire bestanden zijn gecompileerd met Java bytecode versie 1.8 zonder standaardmethoden in interfaces. En ze staan ​​voor kritieke problemen om het op te lossen.


Antwoord 5

Sinds Kotlin 1.4.0 kunt u een van de volgende compilervlaggen gebruiken:

  • -Xjvm-default=all
  • -Xjvm-default=all-compatibility(voor binaire compatibiliteit met oude Kotlin-code)

Hierdoor wordt de compilatie van de JVM-standaardmethode voor alle interfaces ingeschakeld.

Als je wilt lezen hoe je deze vlaggen in je IDE- of Maven/Gradle-project kunt instellen, bekijk dan de documentatie over compileropties.

De voortgang hiervan wordt bijgehouden in uitgave KT-4779, waarin ook een handige samenvatting van de huidige staat. De annotatie @JvmDefaulten de oudere compilervlaggen -Xjvm-default=enableen -Xjvm-default=compatibilitymogen niet langer worden gebruikt.

Other episodes