型变在 Java 中的体现
PECS 原则:Producer Extends Consumer Super
如果需要取值, 应使用 ? extends T
作为数据结构泛型。
如果需要写值, 应使用 ? super T
作为数据结构泛型。
// java.util.Collections#copy
public static <T> void copy(List<? super T> dest, List<? extends T> src)
在 Java 中,数组的类型只支持协变,不支持逆变,而列表的泛型是不可变的。
Number[] nums = new Integer[10]; // ok
List<Number> numList = new ArrayList<Integer>(); // compile failed
数组支持协变因此在写操作时存在一些问题:
Number[] nums = new Integer[10];
nums[0] = 0;
nums[1] = 1.1; // compile success
上面这段代码可以通过编译, 但是在运行时将抛出 ArrayStoreException
, 因为一个 Integer
的数组中无法存放 Double
类型的元素,因此对支持协变的类型进行写操作是不安全的。
在 Java 中泛型自身是不支持型变的, 所以对于泛型来说不存在这个问题,而型变又是十分常用的特性, 借助型变可以帮助我们编写更加通用的代码。所以 Java 中支持在泛型中使用通配符(上界和下界)来实现泛型的协变和逆变。
List<?>
等同于List<? extends Object>
List<? extends Number> numList = new ArrayList<Integer>();
List<? super Integer> intList = new ArrayList<Number>();
通过前面数组协变的例子可以得知: 对于协变来说写操作是不安全的, 同理: 对于逆变来说读操作是不确定的(对于使用泛型下界的例子来说, 我们无法确定从中获取到的数据的具体类型), 因此 Java 在编译器层面就禁止对使用泛型上界的对象进行写入操作, 从而保证了协变下的安全性, 而对于使用泛型下界的对象进行读取操作将返回 Object
类型。
作者:xtyuns
出处:https://www.cnblogs.com/xtyuns/p/16996377.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App