Kotlin中的“双向”类型投影/方差?

时间:2019-05-30 21:56:26

标签: generics kotlin variance

假设我们有一个类似Pug : Dog : Mammal : Animal的类层次结构。

我想要一个接受Bag<T>(其中BagT中不变)参数的函数,该事物是Dog的子OR超类:fun work(things: Bag<in/out Dog) {1}},Bag<Animal>Bag<Dog>可以,但是Bag<Pug>不被接受。

老实说,我什至不确定这种投影的类型。有指针吗?

2 个答案:

答案 0 :(得分:1)

如果您想到inout修饰符的真正含义,您很快就会意识到拥有in OR out并没有任何意义。


我们首先来看一下out。拥有<out T>并不意味着“此函数接受T及其子类作为参数”。它实际上意味着“此函数将不会对类T的参数执行任何操作,或其任何子类都无法执行”。 此修饰符对您可以对该参数执行的操作施加限制。

检查此代码:

fun doThingsWithBag(bag: Bag<out Dog>) {
    bag.getAny().walk() // allowed

    bag.put(Dog()) // not allowed
}

您不允许将任何狗放入该袋中,因为该袋实际上可能是Bag<Pug>类型,并且Bag<Pug>不会接受随机的Dog


in也是如此。 <in T>并不意味着“此函数接受T及其上级”。它更像是“此函数将不会执行任何操作T,或其任何超类都无法执行”。

fun doThingsWithBag(bag: Bag<in Dog>) {
    bag.put(Dog()) // allowed

    bag.getAny().walk() // not allowed
}

现在您可以将狗放在包中了,但是您对狗放在包中实际上无法做任何事情,因为包实际上可能是Bag<Animal>,并且不能保证您所“藏的东西”从书包中取出的是Dog

正如您所看到的,inout可以做什么之间有明显的区别,因此它们不能共存。


即使另一个答案中建议的接口也不能帮上大忙,因为您的Animal类还需要实现相同的接口,这意味着Cat也可以访问该接口。解决的唯一方法是让您的work方法接受Bag<Any>并手动检查其通用类。

更新

您要执行的操作在OOP中是不可能的。您要找的基本上是这样的:

update(Animal()) // allowed
update(Dog())    // allowed
update(Pug())    // allowed
update(Cat())    // not allowed (compile time error)

但是,编译器无法阻止您执行此操作:

val animal: Animal = Cat()
update(animal)  // ???

您只能在运行时通过手动检查输入类型来执行类似的操作。

答案 1 :(得分:0)

  

老实说,我什至不确定这种投影的类型。有指针吗?

对我来说,您需要的是使用Interfaces ,方法是在定义Interface方法的地方创建一个work。这很好,因为它遵循Composition over Inheritance原则

在那之后,让您要使用它的类实现该接口(例如class Dog : Mammal(), WorkInterface),并且不要在您不想/不需要使用work的那些中实现它。

还要让它们覆盖 work方法。这样,您将可以在需要时在对象实例上调用work方法。例如,您的代码应如下所示:

WorkInterface界面

interface WorkInterface{
    fun work(foo:String)    //add the parameters you need
}

在类似Dog 的类上使用接口:

//implementing and extending are done the same way in Kotlin, after the colon
class Dog : Mammal(), WorkInterface{
    //define your attributes, methods etc..

    //override all the methods defined in the interface
    override fun work(foo: String){
        //implement the specific code you want this to do
    }
}

之后,您可以在Dog之类的myDog.work("whatever")实例上使用。