Kotlin仿制药中“*”与“Any”的区别

时间:2016-10-19 18:24:28

标签: kotlin

我不确定我完全理解SomeGeneric<*>SomeGeneric<Any>之间的区别。我认为“*”代表任何东西(外卡),“Any”代表 ALL 对象继承的对象。所以它们似乎应该是一样的,但它们不是?

3 个答案:

答案 0 :(得分:78)

NAT instance视为一种不仅代表任何类型的方式,而是一些您不知道究竟是什么的固定类型,可能会有所帮助。

例如,类型MutableList<*>代表某事的列表(您不知道究竟是什么)。因此,如果您尝试在此列表中添加内容,则无法成功。它可以是String列表,也可以是Int列表,也可以是其他列表。编译器根本不允许在此列表中放置任何对象,因为它无法验证列表是否接受此类型的对象。但是,如果你试图从这样的列表中获取一个元素,你肯定会得到一个Any?类型的对象,因为Kotlin中的所有对象都继承自Any

答案 1 :(得分:18)

在我认为你暗示的上下文中,SomeGeneric<*>相当于SomeGeneric<out Any?>。 Java等价物是SomeGeneric<? extends Object>

语法称为“星形投影”。以下是官方文档:https://kotlinlang.org/docs/reference/generics.html#star-projections

答案 2 :(得分:18)

理解星形投影(*)的关键是首先正确理解另外两种类型的投影inout。之后,星星投影就变得不言自明了。


了解问题

假设您有一个打算用于存储水果的通用类 Crate。此类在 T 中是不变的。这意味着,这个类可以消费也可以生产T(水果)。换句话说,这个类具有将 T 作为参数(消费)以及返回 T(生产)的函数。 size() 函数与 T 无关,它既不接受 T 也不返回 T

class Crate<T> {
    private val items = mutableListOf<T>()
    fun produce(): T = items.last()
    fun consume(item: T) = items.add(item)
    fun size(): Int = items.size
}

但是,如果您想将这个已经存在的类用作生产者(out T)或消费者(in T),或者不想使用 T 而只是它的T 独立函数,如 size()?不用担心意外使用不需要的功能?

解决方案是,我们通过在使用站点使用方差修饰符 outin* 来投影类型。 Use-site 只是意味着我们在哪里使用 Crate 类。


out 投影是 T

的生产者

通过将 Crate 投影为 out,您是在告诉编译器:“当我不小心使用 Crate 类作为 T 的使用者时,给我一个错误,因为我只想安全地使用该类作为生产者 T":

fun useAsProducer(producer: Crate<out Fruit>) {

    // T is known to be out Fruit, so produces Fruit and its subtypes.
    val fruit = producer.produce()           // OK

    // Fruit is guaranteed. Can use functions and properties of Fruit.
    fruit.getColor()                         // OK
    
    // Consumer not allowed because you don't want to accidentally add
    // oranges, if this is a Crate<Apple>
    producer.consume(Orange())               // Error             
}

in 投影是 T

的消费者

通过将 Crate 投影为 in,您是在告诉编译器:“当我不小心使用 Crate 类作为 T 的生产者时,给我一个错误,因为我只想安全地使用该类作为 T" 的使用者:

fun useAsConsumer(consumer: Crate<in Orange>) {

    // Produces Any?, no guarantee of Orange because this could
    // be a Crate<Fruit> with apples in it.
    val anyNullable = consumer.produce()     // Not useful
    
    // Not safe to call functions of Orange on the produced items.
    anyNullable.getVitaminC()                // Error

    // T is known to be in Orange, so consumes Orange and its subtypes.
    consumer.consume(MandarinOrange())       // OK
}

星投影不是T的生产者,也不是消费者

通过将 Crate 投影为 *,您是在告诉编译器:“当我不小心将 Crate 类用作 {{1} 的生产者或消费者时,给我一个错误因为我不想使用消耗和产生 T 的函数或属性。我只想安全地使用 T-independent 函数和属性,如 T":

size()

fun useAsStar(star: Crate<*>) { // T is unknown, so the star produces the default supertype Any?. val anyNullable = star.produce() // Not useful // T is unknown, cannot access its properties and functions. anyNullable.getColor() // Error // Cannot consume because you don't know the type of Crate. star.consume(Fruit()) // Error // Only use the T-independent functions and properties. star.size() // OK } 不是投影

当你说 Any 时,你不是在投射,你只是在使用原始的不变类 Crate<Any>,它可以产生以及消费 Crate<T>

Any

对于 fun useAsAny(any: Crate<Any>) { // T is known to be Any. So, an invariant produces Any. val anyNonNull = any.produce() // OK // T is known to be Any. So, an invariant consumes Any. any.consume(Fruit()) // OK // Can use the T-independent functions and properties, of course. any.size() // OK } 或任何其他没有方差修饰符 Crate<Apple>inout 的类似类型也是如此,它将消耗和产生该类型 ({ {1}} 在这种情况下)。这不是投影。这解释了 *Apple 之间的区别,您可以并排比较上面的两个代码片段。


申报现场制作人的星光投影

到目前为止,我们看到了 SomeGeneric<*> 类的类型投影 SomeGeneric<Any>outin,它们在声明站点:* 处是不变的。从这里开始,让我们看看星形投影如何处理声明站点上已经是 CrateCrate<T> 且类型参数有边界的类:

声明站点

in

使用站点

out

申报现场消费者的星形投影

声明站点

class ProducerCrate<out T : Fruit> {
    private val fruits = listOf<T>()
    fun produce() : T = fruits.last()
}

使用站点

fun useProducer(star: ProducerCrate<*>) {

    // Even though we project * here, it is known to be at least a Fruit
    // because it's an upper bound at the declaration-site.
    val fruit = star.produce()               // OK

    // Fruit is guaranteed. Can use functions and properties of Fruit.
    fruit.getColor()                         // OK
}

请注意,Kotlin 不支持下限。所以,在上面的 class ConsumerCrate<in T> { private val items = mutableListOf<T>() fun consume(item: T) = items.add(item) fun size(): Int = items.size } 类中,我们不能像 fun useConsumer(consumer: ConsumerCrate<*>) { // Cannot consume anything, because the lower bound is not supported // in Kotlin and T is unknown consumer.consume(Orange()) // Error // Only useful for T-independent functions and properties. consumer.size() // OK } (upper bound) 那样有 ConsumerCrate(lower bound) 这样的东西。


声明站点不变量的星形投影

声明站点

in T super Orange

使用站点

out T : Orange

结论

输入不变量 class ProducerConsumerCrate<T : Fruit> { private val fruits = mutableListOf<T>() fun produce(): T = fruits.last() fun consume(fruit: T) = fruits.add(fruit) } 的投影:

<头>
预测 产生 消耗 行为
fun useProducerConsumer(producerConsumer: ProducerConsumerCrate<*>) { // Even though we project * here, T is known to be at least a Fruit // because it's the upper bound at the declaration-site. val fruit = producerConsumer.produce() // OK // Fruit is guaranteed. Can use functions and properties of Fruit. fruit.getColor() // OK // Consumer not allowed because you don't want to accidentally add // oranges, if this crate is a Crate<Apple>. producerConsumer.consume(Fruit()) // Error } Crate<T> Crate<Fruit> 生产者和消费者
Fruit Fruit Crate<out Fruit> 仅限生产者
Fruit Nothing Crate<in Fruit> 仅限消费者
Any? Fruit Crate<*> 没有生产者和消费者

就是这样!希望有所帮助。