Generics 为什么在“函数参数”中考虑逆变型参数;“出去”;位置?

Generics 为什么在“函数参数”中考虑逆变型参数;“出去”;位置?,generics,kotlin,contravariance,Generics,Kotlin,Contravariance,我很难用英语描述,但问题是: class Consumer<in T> { fun consume(t: T) {} } class Accepter<in T>() { // ERROR: Type parameter T is declared as 'in' but occurs in 'out' position in type Consumer<T> fun acceptWith(value: T, consumer: Co

我很难用英语描述,但问题是:

class Consumer<in T> {
    fun consume(t: T) {}
}

class Accepter<in T>() {
    // ERROR: Type parameter T is declared as 'in' but occurs in 'out' position in type Consumer<T>
    fun acceptWith(value: T, consumer: Consumer<T>) {}
}
类消费者{
乐趣消费(t:t){}
}
类接受程序(){
//错误:类型参数T声明为“in”,但出现在类型使用者的“out”位置
fun acceptWith(值:T,使用者:使用者){}
}
它可以这样修复:

fun <U : T> acceptWith(value: T, consumer: Consumer<U>) {}
fun acceptWith(值:T,消费者:消费者){

但我不明白这个问题。允许
消费者
似乎并不安全。有人能解释一下吗?

论点的位置被称为逆变,因为它的变化方向与类的变化方向相反。这意味着类的超类型可以将参数类型的子类型作为参数,反之亦然

让我们考虑一些实际的参数类型<代码> s>代码>。在本例中,类型

接受者
,它是
接受者
的超类型,必须将
消费者
的子类型作为参数,但对于给定的签名,它将
消费者
,它不是
消费者
的子类型,而是它的超类型

另一个例子是,如果允许,此参数类型将不安全。让我们考虑以下代码实现:<代码>承兑人<代码>和<代码>消费者< /代码>:

class AnyAccepter : Accepter<Any>() {
    override fun acceptWith(value: Any, consumer: Consumer<Any>) {
        consumer.consume(Any())
    }
}

class StringConsumer : Consumer<String>() {
    override fun consume(t: String) {
        println(t.length)
    }
}
fun main() {
    val anyAccepter = AnyAccepter()
    val stringAccepter: Accepter<String> = anyAccepter

    // here we're passing a StringConsumer, but the implementation expects Consumer<Any>
    stringAccepter.acceptWith("x", StringConsumer())
}

允许输入的函数参数在逻辑上等同于函数的返回值,这些值显然处于“输出”位置

考虑这个简单的例子:

interface Worker<in T> {
    fun work(output: Consumer<T>)
}
但是,我们可以通过为函数引入新的类型参数来解决此问题:

interface Worker<in T> {
    fun <U : T> work(output: Consumer<U>)
}

通过引入类型参数
U
,我们可以确保
input
output
彼此一致,但仍然允许
Worker
扩展
Worker

。感谢完整的示例,这让我找到了我想要的摘要:逆变参数和不变参数是函数的输出,与返回类型非常相似,因为它们可以传递值。这相当于
acceptWith():T
,这也是不允许的“我根据从您的示例中学到的知识编写了一个新的答案,我认为更好地回答了我的具体问题,这就是为什么可以在“out”中考虑参数“位置。你阐述了这个问题,并给出了一个很好的例子,说明它失败了,而我正努力做到这一点。然而,你的答案似乎回答了我实际上没有回答的一个问题:“什么是逆变?”谢谢!请仔细阅读我的答案,如果我有任何错误,请告诉我。我仍然觉得方差有点令人困惑,而且它周围的语言很难正确使用。我不想误导任何人。@YonaAppletree是的,这是对同一件事的另一个有效观点。@Ilya谢谢你的解释。我们是否可以假设,如果类接受程序永远不会“打开”,那么该声明将是安全的?
interface Worker<in T> {
    fun work(): T
}
fun bad(anyWorker: Worker<Any>) {
    val stringWorker: Worker<String> = anyWorker
    stringWorker.work(Consumer { value: String -> /* value could be Any since it came from anyWorker! */ })
}
interface Worker<in T> {
    fun <U : T> work(output: Consumer<U>)
}
class Worker<in T> {
    private val inputs = mutableListOf<T>()

    fun <U : T> work(input: U, output: Consumer<U>) {
        inputs += input
        output.accept(input)
    }
}