Kotlin强制实施类成为另一种类型的超类型

时间:2018-07-24 08:37:50

标签: inheritance kotlin default-method

由于java / kotlin中不允许多重继承,因此利用接口默认方法很有用。给出示例:

abstract class Animal { 
    fun beAnimal() { 
        println("I'm animal!") 
    } 
}
abstract class Mammal : Animal()  { 
    fun produceMilk() { 
         beAnimal().apply { println("Freesh milk!") }
    } 
}
abstract class AnimalWithBeak : Animal()  { 
    fun quack() { 
        beAnimal().apply { println("Quack!") }
    } 
}
class Platypus : ??? // I want it to both produce milk and quack!

如上所述,不允许使用多个基类,但是我们可以使用接口:

abstract class Animal { fun beAnimal() { println("I'm animal!") } }

interface Mammal { 
    fun produceMilk() { 
        (this as Animal).beAnimal().apply { println("Freesh milk!") }
    } 
}
interface AnimalWithBeak { 
    fun quack() { 
        (this as Animal).beAnimal().apply { println("Quack!") }
    } 
}
class Platypus : Animal(), Mammal, AnimalWithBeak {
    fun bePlatypus() {
        quack() // ok
        produceMilk() // ok
    }
}

请注意,我并不拥有Animal类,但是我仍然想对其进行子类化,并能够混合使用这些实现。上面的示例非常简单,但是在实际代码中,它将非常有用。

问题是,不扩展Animal的类可以实现MammalAnimalWithBeak接口。在这种情况下,代码将被破坏,因为this as Animal强制转换将失败。

那么,问题-是否可以仅将接口继承限制为特定类?在这种情况下,只应允许扩展Animal的类来实现MammalAnimalWithBeak接口。

可能不存在的抽象语法可能看起来像这样:

interface Mammal where this : Animal

但是我怀疑这是无效的。有解决方案吗?

2 个答案:

答案 0 :(得分:3)

您不能限制接口可以实现的类。 但是,如果要避免强制转换,则可以为接口提供类型Animal的属性,实现类必须重写该属性。这至少将确保实现类具有一个Animal对象可用。

abstract class Animal { fun beAnimal() { println("I'm animal!") } }

interface Mammal { 
    val animal: Animal

    fun produceMilk() { 
        animal.beAnimal().apply { println("Freesh milk!") }
    } 
}

interface AnimalWithBeak {
    val animal: Animal

    fun quack() { 
        animal.beAnimal().apply { println("Quack!") }
    } 
}

class Platypus : Animal(), Mammal, AnimalWithBeak {
    override val animal = this

    fun bePlatypus() {
        quack() // ok
        produceMilk() // ok
    }
}

答案 1 :(得分:3)

您可以使用扩展功能来完成这种约束,但是您可能再也不能提供接口方法了。 但是,由于Animal类不受您的控制,因此您可能希望通过扩展功能添加其他一些有用的方法。

示例:

fun <T> T.produceMilk() where T : Animal, T : Mammal {
  beAnimal().apply { println("Freesh milk!") }
}

fun <T> T.quack() where T : Animal, T : AnimalWithBeak {
  beAnimal().apply { println("Quack!") }
}

fun main(args: Array<String>) {
  val myMammal = object : Mammal {} // a mammal, but not an animal
  // myMammal.produceMilk() // unresolved reference
  val myMammalAnimal = Platypus()
  myMammalAnimal.produceMilk() // works
}

您的类/接口如下:

abstract class Animal { fun beAnimal() { println("I'm animal!") } }

interface Mammal
interface AnimalWithBeak 
class Platypus : Animal(), Mammal, AnimalWithBeak {
  fun bePlatypus() {
    quack() // ok
    produceMilk() // ok
  }
}

您所要求的强制紧密耦合可以通过@marstrans答案来完成。该解决方案迫使您在实现接口时总是怀有幻想。