转载:Java中List<T>和List<?>的区别

List<T>、List<?>、List<Object>这三者都可以容纳所有的对象,但使用的顺序应该是首选List<T>,次之List<?>,最后选择List<Object>,原因如下:

(1) List<T>是确定的某一个类型

List<T>表示的是List集合中的元素都为T类型,具体类型在运行期决定;List<?>表示 的是任意类型,与List<T>类似,而List<Object>则表示List集合中的所有元素为Object类 型,因为Object是所有类的父类,所以LiSt<Object>也可以容纳所有的类类型,从这一字面 意义上分析,List<T>更符合习惯:编码者知道它是某一个类型,只是在运行期才确定而已。

(2) List<T>可以进行读写操作

List<T>可以进行诸如add、remove等操作,因为它的类型是固定的T类型,在编码期 不需要进行任何的转型操作。

List<?>是只读类型的,不能进行增加、修改操作,因为编译器不知道List中容纳的是 什么类型的元素,也就无毕校验类型是否安全了,而且List<?>读取出的元素都是Object类 型的,需要主动转型,所以它经常用于泛型方法的返回值。注意,List<?>虽然无法增加、修 改元素,但是却可以删除元素,比如执行remove、clear等方法,那是因为它的删除动作与泛型类型无关

List<Object>也可以读写操作,但是它执行写入操作时需要向上转型(Upcast),在读 取数据后需要向下转型(Downcast),而此时已经失去了泛型存在的意义了。

打个比方,有一个篮子用来容纳物品,List<T>的意思是说,“嘿,我这里有一个篮子, 可以容纳固定类别的东西,比如西瓜、番茄等”。List<?>的意思是说“嘿,我也有一个篮子, 我可以容纳任何东西,只要是你想得到的”。而List<Object>就更有意思了,它说“嘿,我 也有一个篮子,我可以容纳所有物质,只要你认为是物质的东西就都可以容纳进来”。

推而广之,Dao<T>应该比Dao<?>、Dao<Object>更先采用,Desc<Person>则比Desc<?>、Desc<Object> 更优先采用。
---------------------
作者:你是我的天晴
来源:优快云
原文:https://blog.youkuaiyun.com/lexang1/article/details/49593011
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://www.cnblogs.com/stitchZsx/p/9883410.html

你遇到的错误: ``` Required type: List<capture of ?> Provided: List<IBaseType<?>> ``` 通常出现在使用泛型通配符(`?`)时,编译器无法确定具体的类型边界,尤其是在 **方法返回或参数传递中涉及泛型协变(covariance)** 的场景。 --- ### ❓ 问题背景 假设你有一个方法定义如下: ```java public List<? extends IBaseType<?>> getContent() ``` 或者你的类结构类似: ```java class Cell { public List<IBlock<?>> getContent() { ... } } ``` 但在某个上下文中,你试图将其赋值给一个 `List<? extends IBaseType<?>>` 类型的变量,或作为参数传入要求 `List<T>` 的方法,就会出现“capture of ?”这种捕获异常。 --- ### ✅ 常见出错代码示例 ```java // 假设 TableNode.getContent() 返回 List<? extends TableRow> List<? extends TableRow> rows = tableNode.getContent(); for (TableRow row : rows) { List<? extends Cell> cells = row.getContent(); // OK for (Cell cell : cells) { List<IBlock<?>> blocks = cell.getContent(); // OK // 错误可能出现在这里: List<? extends IBaseType<?>> content = cell.getContent(); // ❌ 编译错误:Required type: List<? extends IBaseType<?>>, Provided: List<IBlock<?>> } } ``` 即使 `IBlock<?>` 实现了 `IBaseType<?>`,Java 泛型不支持自动协变转换: > `List<IBlock<?>>` **不能赋值给** `List<? extends IBaseType<?>>` > 尽管 `IBlock<X> implements IBaseType<X>` 这是因为泛型是不可变的(invariant)。 --- ### ✅ 正确解决方案 #### ✅ 方案一:强制类型匹配(推荐重构) 确保接口继承关系在泛型上一致,并避免中间类型断开。 ##### 1. 定义清晰的继承关系: ```java interface IBaseType<T> { } interface IBlock<T> extends IBaseType<T> { } ``` ##### 2. 修改 `Cell.getContent()` 返回更宽泛的类型: ```java class Cell { private List<IBlock<?>> content; public List<? extends IBaseType<?>> getContent() { return content; // ✅ 允许:IBlock<?> 是 IBaseType<?> 的子类型 } } ``` 这样你就可以安全地赋值: ```java List<? extends IBaseType<?>> types = cell.getContent(); // ✅ 成功 ``` --- #### ✅ 方案二:使用辅助方法进行类型转换(不修改 API) 如果你无法修改 `getContent()` 的返回类型,可以用泛型辅助方法“欺骗”编译器: ```java @SuppressWarnings("unchecked") private static <T extends IBaseType<?>> List<T> asBaseTypeList(List<? extends IBaseType<?>> list) { return (List<T>) list; } // 使用时: List<IBlock<?>> blocks = cell.getContent(); List<? extends IBaseType<?>> baseTypes = blocks; // ✅ 如果 IBlock<?> extends IBaseType<?> ``` 但前提是 `IBlock` 确实实现了 `IBaseType`。 如果没实现,请先补全: ```java interface IBlock<T> extends IBaseType<T> { } ``` --- #### ✅ 方案三:绕过泛型限制,用 Stream 映射(函数式风格) ```java List<String> texts = cell.getContent().stream() .filter(block -> block instanceof Paragraph) .map(block -> (Paragraph) block) .flatMap(paragraph -> paragraph.getContent().stream()) .flatMap(inline -> inline.getContent().stream()) .collect(Collectors.toList()); ``` 这适用于你只关心最终字符串而不强求中间泛型匹配的情况。 --- ### 🔍 根本原因解释 Java 泛型是**不可变的**: - `List<String>` 不是 `List<Object>` 的子类型。 - 同理:`List<IBlock<?>>` 不是 `List<? extends IBaseType<?>>` 的子类型, 即使 `IBlock<?>` 实现了 `IBaseType<?>`。 要让这个成立,必须满足两点: 1. `IBlock<X> implements IBaseType<X>` 2. 方法返回类型声明为 `List<? extends IBaseType<?>>` 否则就必须手动转换或重构。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值