我不知道我完全理解之间的差异SomeGeneric<*>
和SomeGeneric<Any>
.我认为"*"代表任何东西(外卡),"Any"代表ALL对象继承的对象.所以它们似乎应该是一样的,但它们不是?
Ale*_*lov 110
将星形投影视为一种表示不仅仅是任何类型的方法,而是一些你不知道究竟是什么类型的固定类型可能会有所帮助.
例如,类型MutableList<*>
表示某事物的列表(您不知道究竟是什么).因此,如果您尝试在此列表中添加内容,则不会成功.它可以是String
s 的列表,也可以是s的列表Int
,或者其他的列表.编译器根本不允许在此列表中放置任何对象,因为它无法验证列表是否接受此类型的对象.但是,如果你试图从这样的列表中获取一个元素,你肯定会得到一个类型的对象Any?
,因为Kotlin中的所有对象都继承自Any
.
Yog*_*ity 25
理解星形投影(*
)的关键是正确理解其他两种类型的投影in
和out
第一。在那之后,星形投影变得不言自明。
假设您有一个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
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您想将这个已经存在的类用作生产者(out T
)或消费者(in T
),或者不想T
仅使用其与 T 无关的函数,该size()
怎么办?不用担心意外使用不需要的功能?
解决的办法是,我们采用方差改性剂项目的类型out
,in
并*
在使用现场。Use-site只是意味着我们在哪里使用这个Crate
类。
out
投影是一个生产者 T
通过投影Crate
as 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
}
Run Code Online (Sandbox Code Playgroud)
in
投影是消费者 T
通过投影Crate
as 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
}
Run Code Online (Sandbox Code Playgroud)
T
通过投影Crate
as *
,您是在告诉编译器:“当我不小心使用Crate
该类作为生产者或消费者时给我一个错误,T
因为我不想使用消耗和生产的函数或属性T
。我只想安全地使用与 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
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
对于Crate<Apple>
没有变化修饰符in
, out
, or 的或 任何其他类似类型也是如此*
,它将消耗并生成该类型(Apple
在这种情况下)。这不是投影。这解释了SomeGeneric<*>
和之间的区别SomeGeneric<Any>
,您可以并排比较上面的两个代码片段。
到目前为止,我们看到了类型的预测out
,in
并且*
为Crate
这是在声明站点不变类:Crate<T>
。从这里开始,让我们找出星形投影如何处理已经存在的类in
以及out
在声明站点上的具有类型参数边界的类:
申报现场
class ProducerCrate<out T : Fruit> {
private val fruits = listOf<T>()
fun produce() : T = fruits.last()
}
Run Code Online (Sandbox Code Playgroud)
使用现场
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
}
Run Code Online (Sandbox Code Playgroud)
申报现场
class ConsumerCrate<in T> {
private val items = mutableListOf<T>()
fun consume(item: T) = items.add(item)
fun size(): Int = items.size
}
Run Code Online (Sandbox Code Playgroud)
使用现场
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
}
Run Code Online (Sandbox Code Playgroud)
请注意,Kotlin 不支持下限。所以,在ConsumerCrate
上面的类中,我们不能像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)
}
Run Code Online (Sandbox Code Playgroud)
使用现场
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
}
Run Code Online (Sandbox Code Playgroud)
输入不变量的投影Crate<T>
:
预测 | 生产 | 消耗 | 行为 |
---|---|---|---|
Crate<Fruit> |
Fruit |
Fruit |
生产者和消费者 |
Crate<out Fruit> |
Fruit |
Nothing |
仅限生产者 |
Crate<in Fruit> |
Any? |
Fruit |
仅限消费者 |
Crate<*> |
Any? |
Nothing |
没有生产者和消费者 |
就是这样!希望有帮助。
vod*_*dan 22
在我认为你暗示的上下文中,SomeGeneric<*>
相当于SomeGeneric<out Any?>
.Java等价物是SomeGeneric<? extends Object>
.
语法称为"星形投影".以下是官方文档:https://kotlinlang.org/docs/reference/generics.html#star-projections
归档时间: |
|
查看次数: |
10732 次 |
最近记录: |