Kotlin强制实现类为另一类型的超类型



由于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

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

您不能约束接口可以由哪些类实现。但是,如果希望避免强制转换,可以为接口提供类型为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
}
}

您可以使用扩展函数来实现这样的约束,但您可能不再提供接口方法。但是,由于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答案来完成。该解决方案迫使您在实现接口时始终使用动物。

最新更新