Kotlin中具有一个类和接口的多重继承

时间:2019-08-27 08:57:05

标签: kotlin

我的预期目标是通过一组固定的属性和方法(其名称不会重叠)扩展现有库中的多个类。

直接的解决方案是简单的继承:派生库类并添加属性/方法。但这将是非常重复的,因为所有派生类的属性和方法都相同。最好创建一个具有我所需的所有属性和方法的辅助类,并简单地从库类和辅助类中派生扩展类。

但是,似乎Kotlin中没有类似于C ++的多重继承,所以我想使用接口来使其工作,在该接口中可以容纳我需要的所有属性和方法。

我已经开始使用以下简单的代码来测试接口:

open class LibraryClass{
    var x: Int = 0

    fun setMyX(x_: Int){
        x = x_
    }
}

interface MyInterface{
    var y: Int
    var z: Int
    var abc: Int

    fun myMethod(y_: Int){
        y = y_
        z = y*y
    }
} 

class InheritanceTest: LibraryClass(), MyInterface{    
    fun print(){
    println("My values: $x, $y, $z")
    }
}

fun main(){
    var test = InheritanceTest()
    test.setMyX(1)
    test.myMethod(5)
    test.print()    
}

如果我尝试编译此代码,则会收到以下错误消息:

error: class 'InheritanceTest' is not abstract and does not implement abstract member public abstract var y: Int defined in MyInterface

一些研究表明,我需要在派生类的主要构造函数中重写接口的属性:

class InheritanceTest(override var y: Int, override var z: Int, override var abc: Int)

然后

var test = InheritanceTest(2,3,0)

在派生类中重写all(?)接口属性(甚至是未使用的接口)似乎是重复的(并且也不可能在接口中初始化属性),这基本上超出了我最初打算使用接口的目的:以节省重复。 (除了方法,似乎只要一个类和(多个)接口不共享相同名称的方法,就不必重写这些方法。)

我所插入的问题是:这是解决问题或解决错误的唯一方法吗?

然后我在界面中找到了其他可以尝试的地方:

var fgh: Int 
    get()  { return fgh }
    set(v) { fgh = v }

到目前为止,没有必要重写。但是我仍然无法获得如何在方法或设置值中实际使用属性fgh的方法。

当我尝试通过test.fgh = 100中的main()设置fgh时,出现以下错误:

Exception in thread "main" java.lang.StackOverflowError
    at MyInterface$DefaultImpls.setFgh(

然后尝试使用test.setFgh(100)(因为先前的错误消息暗示该方法存在)会产生:

error: unresolved reference: setFgh

尝试定义setFgh(v: Int) {fgh = v}会导致:

error: platform declaration clash: The following declarations have the same JVM signature (setFgh(I)V):
    fun <set-fgh>(v: Int): Unit defined in MyInterface
    fun setFgh(v: Int): Unit defined in MyInterface

好的,定义一个类似于myMethod()的不同名称的方法:

fun myOtherMethod(v: Int){    
    fgh = v 
    z = fgh * fgh
}

然后在myOtherMethod(10)中使用main()再次给出:

Exception in thread "main" java.lang.StackOverflowError
    at MyInterface$DefaultImpls.setFgh

因此,我现在真的迷失了如何获得非重复工作解决方案以扩展具有相同属性和方法的多个类的方法。

编辑: 我现在知道接口不是我想要的,因为必须实现(不是重写,这需要我花点时间才能通过关键字“ override”完成)派生类中的所有内容。

3 个答案:

答案 0 :(得分:2)

您的第一次尝试是在正确的轨道上。您确实必须重写接口中的属性,但不必在构造函数中完成。您可以在类主体中执行此操作,并且可以为它们添加默认值(不仅可以,而且必须)。如果您打开该课程,则可以在所有课程中对其进行扩展。

open class InheritanceTest: LibraryClass(), MyInterface {
    override var y: Int = 0
    override var z: Int = 0
    override var abc: Int = 0

    fun print() {
        println("My values: $x, $y, $z")
    }
}

使用该类,您的原始主类将打印My values: 1, 5, 25,并且您可以从该类继承而不必重写/初始化任何内容(但是您可以使用init块来自定义您的超类)。例如:

class MyClass : InheritanceTest() {
    init {
        myMethod(4)
    }
}

fun main() {
    val test = MyClass()
    test.setMyX(2)
    test.print()
}

将打印My values: 2, 4, 16

我希望我能正确理解您的问题,并且可以为您提供帮助:)

答案 1 :(得分:1)

首先,StackOverflowError背后的原因是您正在递归访问setter和getter中的属性:

var fgh: Int 
    get()  { return fgh } // recursive call to getFgh()
    set(v) { fgh = v } // recursive call to setFgh(v)

要在自定义访问器中访问属性的backing field,您需要使用field标识符:

var fgh: Int 
    get()  { return field }
    set(v) { field = value }

但是,在这种情况下,自定义访问器是多余的,重点是引入field标识符。而且,由于接口中的属性无法初始化或没有后备字段,因此在接口中仍然无法使用。

为了或多或少地实现您想要的目标(如果我理解正确的话),您可以创建一个中间类,该中间类可以扩展LibraryClass,可以实现MyInterface并且可以扩展由InheritanceTest

// abstract or open
abstract class InBetweenClass : LibraryClass(), MyInterface {
    override var y: Int = 1
    override var z: Int = 2
    override var abc: Int = 3
}

class InheritanceTest : InBetweenClass() {    
    fun print() {
        println("My values: $x, $y, $z")
    }
}

或使用delegationMyInterface属性提供默认实现:

class DefaultImplementation : MyInterface {
    override var y: Int = 1
    override var z: Int = 2
    override var abc: Int = 3
}

class InheritanceTest : LibraryClass(), MyInterface by DefaultImplementation() {    
    fun print(){
        println("My values: $x, $y, $z")
    }
}

答案 2 :(得分:0)

任何实现接口的具体类都必须实现接口的所有成员,否则它不履行合同。如果您在类中没有实现的接口中具有属性,则您的类不满足该接口,因此会产生编译错误。该接口的任何用户都希望实际实例提供该接口的所有功能,这就是编译器强制执行该接口的原因。

如果要共享某些字段,但不共享所有字段,则可以创建接口层次结构,并仅实现所需的接口。但是,我不完全了解您要达到的目的以及为什么要添加的属性会有如此多的冗余。

扩展库类以添加方法的另一种方法是在库类上定义extension functions/properties,但是您需要重复一些操作,并且无法在其中存储状态。

如果您确实需要存储状态,并且不想手动重新实现所有字段,则还可以使用delegation机制。 创建一个用于实现通用接口的类,并在为lib创建自定义扩展类时提供该类的实例。再说一次,这似乎是解决错误问题的方法,最好在这里真正了解您要实现的目标。