我想了解in和out在 Kotlin 中。正如我发现的理论一样,消费者in需要和生产者out回报。
但是,下面的两种方法如何区分何时in和out被视为我们可以list毫无问题地访问的方法参数?
private fun exampleMethod1(list: ArrayList<out String>) {}
private fun exampleMethod2(list: ArrayList<in String>) {}
Run Code Online (Sandbox Code Playgroud)
Wil*_*zel 16
让我通过一个例子来演示什么in/out做什么。考虑以下:
private fun foo(list: ArrayList<Number>) {}
private fun bar(list: ArrayList<Number>) {}
Run Code Online (Sandbox Code Playgroud)
现在我们尝试向ArrayList每个函数传递一个,每个函数都有一个不同的泛型类型参数:
// Error: Type Mismatch. Required `ArrayList<Number>` Found `ArrayList<Int>`
foo(arrayListOf<Int>())
// Error: Type Mismatch. Required `ArrayList<Number>` Found `ArrayList<Any>`
bar(arrayListOf<Any>())
Run Code Online (Sandbox Code Playgroud)
但是我们得到了错误!我们如何解决这个问题?我们必须以某种方式告诉编译器,因为foo列表还可以包含Number(eg Int)的子类型的元素,因为bar我们必须告诉编译器列表还可以包含Number(eg Any)的基本类型的元素。
private fun foo(list: ArrayList<out Number>) {}
private fun bar(list: ArrayList<in Number>) {}
Run Code Online (Sandbox Code Playgroud)
现在它起作用了!
当修饰符inorout用于函数参数时,称为类型投影。
| 预测 | 生产 | 消耗 | 函数行为 |
|---|---|---|---|
ArrayList<out Orange> |
Orange |
Nothing |
接受子类型 ArrayList<Orange> |
ArrayList<in Orange> |
Any? |
Orange |
接受超类型 ArrayList<Orange> |
ArrayList<Orange> |
Orange |
Orange |
不接受任何子类型或超类型 |
ArrayList在 Kotlin 中既是生产者也是消费者。这是因为它是一个不变的泛型类,定义为ArrayList<T>,而不是ArrayList<out T>(生产者)或ArrayList<in T>(消费者)。这意味着该类可以具有接受T作为函数参数(消费)或返回T(生产)的函数。
但是,如果您想安全地将这个已经存在的类用作消费者(in T)或生产者(out T),该怎么办?无需担心意外使用其他不需要的功能?
在这种情况下,我们通过使用差异修饰符in和out
在使用地点来投影类型。Use-site只是意味着我们在哪里使用这个ArrayList<T>类。
out产生T并且函数接受子类型当您将ArrayList用作生产者( out) 时,该函数可以接受 的子类型ArrayList<Orange>,即ArrayList<MandarinOrange>, ArrayList<BloodOrange>,因为MandarinOrange和BloodOrange是 的子类型Orange。因为保留了子类型:
fun useAsProducer(producer: ArrayList<out Orange>) {
// Producer produces Orange and its subtypes
val orange = producer.get(1) // OK
// Can use functions and properties of Orange
orange.getVitaminC() // OK
// Consumer functions not allowed
producer.add(BloodOrange()) // Error
}
Run Code Online (Sandbox Code Playgroud)
该producer生产Orange和它的亚型。在这里producer.get(1)可以返回MandarinOrange,BloodOrange等等。但我们不只要我们得到的关心Orange。因为我们只对使用Orangeat use-site的属性和功能感兴趣。
编译器不允许调用add()函数(消费者),因为我们不知道Orange它包含哪种类型。您不想意外添加BloodOrange,如果这是一个ArrayList<MandarinOrange>.
in消耗T并且函数接受超类型当你ArrayList作为消费者(in)使用时,该函数可以接受 的超类型ArrayList<Orange>,也就是说,ArrayList<Fruit>因为现在子类型被颠倒了。这意味着ArrayList<Fruit>是ArrayList<Orange>whenOrange的子类型是 的子类型Fruit:
fun useAsConsumer(consumer: ArrayList<in Orange>) {
// Produces Any?, no guarantee of Orange because this could
// be an ArrayList<Fruit> with apples in it
val anyNullable = consumer.get(1) // Not useful
// Not safe to call functions of Orange on the produced items.
anyNullable.getVitaminC() // Error
// Consumer consumes Orange and its subtypes
consumer.add(MandarinOrange()) // OK
}
Run Code Online (Sandbox Code Playgroud)
在consumer消耗Orange它的亚型。是 aMandarinOrange还是 a都没有关系BloodOrange,只要它是Orange. 因为consumer只Orange对其声明站点的属性和功能感兴趣。
编译器确实允许调用get()函数(生产者),但它产生Any?对我们没有用的。当您将其Any?用作Orangeat use-site时,编译器会标记错误。
T,函数不接受子类型或超类型当您ArrayList用作生产者和消费者(没有in或out)时,该函数只能接受确切的类型ArrayList<Orange>,而不能接受其他子类型(如 )ArrayList<MandarinOrange>或超类型(如 )ArrayList<Fruit>。因为不变量不允许子类型化:
fun useAsProducerConsumer(producerConsumer: ArrayList<Orange>) {
// Produces Orange and its subtypes
val orange = producerConsumer.get(1) // OK
// Orange is guaranteed
orange.getVitaminC() // OK
// Consumes Orange and its subtypes
producerConsumer.add(Orange()) // OK
}
Run Code Online (Sandbox Code Playgroud)
不变式生产和消费Orange及其子类型。
就是这样!类型投影就是告诉编译器您如何在该特定函数中使用该类,因此如果您不小心调用了非预期的函数,它可以通过标记错误来帮助您。希望有帮助。
| 归档时间: |
|
| 查看次数: |
2622 次 |
| 最近记录: |