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

xin*_*aiz 6 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!
Run Code Online (Sandbox Code Playgroud)

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

interface Mammal where this : Animal

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

mar*_*ran 5

您无法限制接口可以由哪些类实现。但是,如果您想避免强制转换,可以为接口提供一个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
    }
}
Run Code Online (Sandbox Code Playgroud)


Rol*_*and 5

您可以使用扩展函数来完成这样的约束,但您可能不再提供接口方法。但是,由于Animal-class 不受您的控制,您可能需要通过扩展函数添加一些您想要的其他有用方法。

例子:

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
}
Run Code Online (Sandbox Code Playgroud)

您的类/接口将如下所示:

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

interface Mammal
interface AnimalWithBeak 
class Platypus : Animal(), Mammal, AnimalWithBeak {
  fun bePlatypus() {
    quack() // ok
    produceMilk() // ok
  }
}
Run Code Online (Sandbox Code Playgroud)

您所要求的强制紧密耦合可以通过 @marstrans 答案来完成。该解决方案迫使您在实现界面时始终有一只动物。

  • 啊,这是扩展函数的一个很好的用途。 (2认同)