关于Kotlin中已out协变类型添加out投影冗余的疑问求解
关于Kotlin中List冗余投影的问题解答
嘿,这个问题问到点子上了!我来一步步帮你拆解清楚~
首先先快速回顾下基础:Kotlin里的List接口本身就被声明为interface List<out T>,这里的out已经明确了T是协变类型参数——也就是说,List<Dog>可以安全地赋值给List<Animal>(假设Dog是Animal的子类),因为List里所有用到T的地方都是作为返回值(比如get(index: Int): T),不会有修改集合的操作(毕竟List是只读的)。
问题1:为什么有人会给已协变的List加out投影?
主要有这几种场景:
- 新手认知误区:刚接触Kotlin泛型变异的开发者,可能还没完全搞清楚“类声明时的协变”和“使用时的投影”的区别,以为要手动加
out才能让List具备协变能力,其实List本身已经自带这个特性了。 - 复制粘贴或习惯使然:在处理可变集合(比如
MutableList,它不是协变的)时,我们经常会写MutableList<out T>来做临时协变投影(限制只能读取不能写入),有些开发者可能会把这个习惯带到List上,就导致了冗余的out。 - 复杂泛型场景的惯性:在一些泛型函数或者嵌套泛型的代码里,可能会下意识地加上投影修饰符,但对于本身已经协变的类型来说,这个操作完全没必要。
问题2:为什么List和List等价?
因为List<out T>的约束和List<T>本身的约束完全一致:
- List类声明时的
out T已经保证了T只能出现在返回值位置,不会有任何方法把T作为入参(比如MutableList的add(element: T)就不存在于List中)。 - 当你给List再加一层
out投影时,并没有给类型增加任何新的限制,编译器能识别到这一点,所以会抛出“冗余投影”的警告。从类型兼容性和行为上来看,List<out String>和List<String>是完全相同的类型,没有任何区别。
举个简单的例子验证:
val strList: List<String> = listOf("a", "b") val outStrList: List<out String> = strList // 合法,但编译器会警告冗余 val animalList: List<CharSequence> = outStrList // 和直接用strList赋值的效果完全一样
内容的提问来源于stack exchange,提问作者sunflower20
相关产品推荐
相关产品推荐

