- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
原始列表转换为 List<?>
正好。为什么原始列表的列表不能转换为 List<?>
的列表?
{ // works
List raw = null;
List<?> wild = raw;
}
{ // Type mismatch: cannot convert from List<List> to List<List<?>>
List<List> raw = null;
List<List<?>> wild = raw;
}
背景故事(缓解 the xy problem ):
我正在使用的 API 返回 List<JAXBElement>
.我碰巧知道它总是List<JAXBElement<String>>
.我计划循环构建自己的 List<String>
,但我在编写 List<JAXBElement> raw = api();
时试图修复(但不抑制)原始类型编译器警告.
我试过:
List<JAXBElement<?>> raw = api();
List<JAXBElement<?>> raw = (List<JAXBElement<?>>) api();
但是这些给出了类型不匹配错误。
有趣的是,这没有给出警告或错误:
for (JAXBElement<?> e : api()) {
// ...
}
最佳答案
// #1 (does compile)
List raw = null;
List<?> wild = raw;
// #2 (doesn't compile)
List<List> raw = null;
List<List<?>> wild = raw;
首先让我们理清为什么这些实际上是不相关的作业。也就是说,它们受不同规则的约束。
#1 称为 unchecked conversion :
There is an unchecked conversion from the raw class or interface type (§4.8)
G
to any parameterized type of the formG<T<sub>1</sub>,...,T<sub>n</sub>>
.
具体来说,它是 assignment context 的特例仅适用于这种情况:
If, after [other possible conversions] have been applied, the resulting type is a raw type, an unchecked conversion may then be applied.
#2需要引用类型转换;然而问题在于它不是 widening conversion (这是一种在没有转换的情况下隐式允许的引用转换)。
这是为什么呢?好吧,这是由 rules of generic subtyping 专门管理的更具体地说,这个要点:
Given a generic type declaration
C<F<sub>1</sub>,...,F<sub>n</sub>>
(n > 0), the direct supertypes of the parameterized typeC<T<sub>1</sub>,...,T<sub>n</sub>>
, whereT<sub>i</sub>
(1 ≤ i ≤ n) is a type, are all of the following:
C<S<sub>1</sub>,...,S<sub>n</sub>>
, whereS<sub>i</sub>
containsT<sub>i</sub>
(1 ≤ i ≤ n).
这指的是 JLS 称为 containment 的东西,要成为有效的赋值,左侧的参数必须包含右侧的参数。自 "concrete" generic types 以来,遏制在很大程度上控制了通用子类型化是invariant .
您可能熟悉以下想法:
List<Dog>
不是 List<Animal>
List<Dog>
是 List<? extends Animal>
.嗯,后者是正确的,因为 ? extends Animal
包含 Dog
.
所以问题变成了“类型参数 List<?>
是否包含原始类型参数 List
”?答案是否定的:虽然 List<?>
是 List
的子类型,这种关系不适用于类型参数。
没有特殊规则使它成立:List<List<?>>
不是 List<List>
的子类型出于基本相同的原因List<Dog>
不是 List<Animal>
的子类型.
所以因为List<List>
不是 List<List<?>>
的子类型, 赋值无效。同样,您不能直接执行 narrowing conversion类型转换因为List<List>
不是 List<List<?>>
的父类(super class)型要么。
要进行分配,您仍然可以应用强制转换。有 3 种方法在我看来是合理的。
// 1. raw type
@SuppressWarnings("unchecked")
List<List<?>> list0 = (List) api();
// 2. slightly safer
@SuppressWarnings({"unchecked", "rawtypes"})
List<List<?>> list1 = (List<List<?>>) (List<? extends List>) api();
// 3. avoids a raw type warning
@SuppressWarnings("unchecked")
List<List<?>> list2 = (List<List<?>>) (List<? super List<?>>) api();
(您可以用 JAXBElement
代替内部的 List
。)
此转换的用例应该是安全的,因为 List<List<?>>
是比 List<List>
更严格的类型.
原始类型 语句是一个扩大的转换然后是未经检查的赋值。这是可行的,因为如上所示,任何参数化类型都可以转换为其原始类型,反之亦然。
slightly safe 语句(之所以这样命名是因为它丢失的类型信息较少)是先扩大转换再缩小转换。这通过转换为通用父类(super class)型来实现:
List<? extends List>
╱ ╲
List<List<?>> List<List>
有界通配符允许类型参数被考虑用于通过包含进行子类型化。
事实List<? extends List>
被认为是 List<List<?>>
的父类(super class)型可以用传递性证明:
? extends List
包含 ? extends List<?>
,因为 List
是 List<?>
的父类(super class)型.
? extends List<?>
包含 List<?>
.
因此? extends List
包含 List<?>
.
(即 List<? extends List> :> List<? extends List<?>> :> List<List<?>>
。)
第三个示例的工作方式与第二个示例类似,转换为通用父类(super class)型 List<? super List<?>>
。 .由于它不使用原始类型,因此我们可以减少一个警告。
这里的非技术总结是规范暗示List<List>
之间既没有子类型也没有父类(super class)型关系。和 List<List<?>>
.
尽管从 List<List>
转换而来至 List<List<?>>
应该是安全的,这是不允许的。 (这是安全的,因为两者都是 List
,可以存储任何类型的 List
,但是 List<List<?>>
对其元素在检索后的使用方式施加了更多限制。)
不幸的是,除了原始类型很奇怪并且它们的使用存在问题之外,没有实际原因导致编译失败。
关于java - 无法从 List<List> 转换为 List<List<?>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52523554/
我通过 spring ioc 编写了一些 Rest 应用程序。但我无法解决这个问题。这是我的异常(exception): org.springframework.beans.factory.BeanC
我对 TestNG、Spring 框架等完全陌生,我正在尝试使用注释 @Value通过 @Configuration 访问配置文件注释。 我在这里想要实现的目标是让控制台从配置文件中写出“hi”,通过
为此工作了几个小时。我完全被难住了。 这是 CS113 的实验室。 如果用户在程序(二进制计算器)结束时选择继续,我们需要使用 goto 语句来到达程序的顶部。 但是,我们还需要释放所有分配的内存。
我正在尝试使用 ffmpeg 库构建一个小的 C 程序。但是我什至无法使用 avformat_open_input() 打开音频文件设置检查错误代码的函数后,我得到以下输出: Error code:
使用 Spring Initializer 创建一个简单的 Spring boot。我只在可用选项下选择 DevTools。 创建项目后,无需对其进行任何更改,即可正常运行程序。 现在,当我尝试在项目
所以我只是在 Mac OS X 中通过 brew 安装了 qt。但是它无法链接它。当我尝试运行 brew link qt 或 brew link --overwrite qt 我得到以下信息: ton
我在提交和 pull 时遇到了问题:在提交的 IDE 中,我看到: warning not all local changes may be shown due to an error: unable
我跑 man gcc | grep "-L" 我明白了 Usage: grep [OPTION]... PATTERN [FILE]... Try `grep --help' for more inf
我有一段代码,旨在接收任何 URL 并将其从网络上撕下来。到目前为止,它运行良好,直到有人给了它这个 URL: http://www.aspensurgical.com/static/images/a
在过去的 5 个小时里,我一直在尝试在我的服务器上设置 WireGuard,但在完成所有设置后,我无法 ping IP 或解析域。 下面是服务器配置 [Interface] Address = 10.
我正在尝试在 GitLab 中 fork 我的一个私有(private)项目,但是当我按下 fork 按钮时,我会收到以下信息: No available namespaces to fork the
我这里遇到了一些问题。我是 node.js 和 Rest API 的新手,但我正在尝试自学。我制作了 REST API,使用 MongoDB 与我的数据库进行通信,我使用 Postman 来测试我的路
下面的代码在控制台中给出以下消息: Uncaught DOMException: Failed to execute 'appendChild' on 'Node': The new child el
我正在尝试调用一个新端点来显示数据,我意识到在上一组有效的数据中,它在数据周围用一对额外的“[]”括号进行控制台,我认为这就是问题是,而新端点不会以我使用数据的方式产生它! 这是 NgFor 失败的原
我正在尝试将我的 Symfony2 应用程序部署到我的 Azure Web 应用程序,但遇到了一些麻烦。 推送到远程时,我在终端中收到以下消息 remote: Updating branch 'mas
Minikube已启动并正在运行,没有任何错误,但是我无法 curl IP。我在这里遵循:https://docs.traefik.io/user-guide/kubernetes/,似乎没有提到关闭
每当我尝试docker组成任何项目时,都会出现以下错误。 我尝试过有和没有sudo 我在这台机器上只有这个问题。我可以在Mac和Amazon WorkSpace上运行相同的容器。 (myslabs)
我正在尝试 pip install stanza 并收到此消息: ERROR: No matching distribution found for torch>=1.3.0 (from stanza
DNS 解析看起来不错,但我无法 ping 我的服务。可能是什么原因? 来自集群中的另一个 Pod: $ ping backend PING backend.default.svc.cluster.l
我正在使用Hibernate 4 + Spring MVC 4当我开始 Apache Tomcat Server 8我收到此错误: Error creating bean with name 'wel
我是一名优秀的程序员,十分优秀!