型变在 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 国际」许可协议进行许可。

posted @   xtyuns  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示