You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

泛型方差场景下ArrayList与List的差异及编译报错原因

问题:为什么Wrapper1编译报错?ArrayList与List的差异是什么?

先看两段代码:

可正常编译的代码

class Wraper {
    private fun foo(list: ArrayList<out Number>) {    }
    private fun bar(list: ArrayList<in Number>) {    }

    fun doSome() {
        foo(arrayListOf<Int>())
        bar(arrayListOf<Any>())
    }
}

编译报错的代码

// error:
// Projection is conflicting with variance of the corresponding 
// type parameter of List. Remove the projection or replace it with '*'
class Wrapper1 {
    private fun foo(list: List<out Number>) {    }
    private fun bar(list: List<in Number>) {    } // error

    fun doSome() {
        foo(listOf<Int>())
        bar(listOf<Any>()) 
    }
}

报错原因

核心问题出在List和ArrayList的泛型方差声明不同:

  1. ArrayList是不变类:它的泛型参数没有默认的out/in标记,属于**不变(invariant)**类型。因此你可以通过手动添加out/in投影,临时改变它的协变/逆变特性——比如ArrayList<out Number>表示这个集合只能作为生产者(读取Number类型的值),ArrayList<in Number>表示只能作为消费者(写入Number类型的值),这两种写法都是合法的。
  2. List是协变接口:Kotlin中List接口的定义是interface List<out E>,泛型参数E天生被标记为out(协变)。这意味着List本身只能作为生产者,不能被当作消费者使用。此时你给它添加in Number投影,就和它原本的协变声明冲突了,编译器自然会报错。

另外,Wrapper1中的foo(list: List<out Number>)写法是冗余但合法的——因为List已经是协变的,重复加out不会冲突,但完全没必要。

ArrayList与List的关键差异

  • 泛型方差特性:
    • List是只读接口,天生协变(out E),因此你可以安全地将List<Int>赋值给List<Number>,因为只读集合不会出现类型不安全的写入操作。
    • ArrayList是可变集合的具体实现类,泛型参数默认不变,没有out/in标记。如果需要临时调整它的协变/逆变特性,必须手动添加投影。
  • 读写权限:
    • List只提供只读方法(如get()、size),协变特性保证了类型安全。
    • ArrayList是MutableList的实现,支持读写操作,不变的泛型参数避免了像List<in Number>这种矛盾的用法,防止不安全的写入。

内容的提问来源于stack exchange,提问作者BollMose

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 17:58:17