泛型的泛型如何工作?

2024-01-13

虽然我确实了解泛型的一些极端情况,但我在以下示例中遗漏了一些内容。

我有以下课程

1 public class Test<T> {
2   public static void main(String[] args) {
3     Test<? extends Number> t = new Test<BigDecimal>();
4     List<Test<? extends Number>> l =Collections.singletonList(t);
5   }
6 }

第 4 行给了我错误

Type mismatch: cannot convert from List<Test<capture#1-of ? extends Number>> 
to List<Test<? extends Number>>`. 

显然,编译器认为不同的?并不真正平等。虽然我的直觉告诉我,这是正确的。

谁能提供一个示例,如果第 4 行合法,我会收到运行时错误?

EDIT:

为了避免混淆,我替换了=null在第 3 行中通过具体赋值


正如肯尼在他的评论中指出的,你可以通过以下方式解决这个问题:

List<Test<? extends Number>> l =
    Collections.<Test<? extends Number>>singletonList(t);

这立即告诉我们该操作不是unsafe,它只是有限的受害者推理。如果不安全,上面的代码就无法编译。

由于在上面的泛型方法中使用显式类型参数只需要充当hint,我们可以推测这里需要的是推理引擎的技术限制。事实上,Java 8 编译器目前预计将附带对类型推断的许多改进 http://openjdk.java.net/jeps/101。我不确定您的具体情况是否会得到解决。

那么,到底发生了什么?

好吧,我们收到的编译错误表明类型参数T of Collections.singletonList被推断为capture<Test<? extends Number>>。换句话说,通配符具有一些与其关联的元数据,将其链接到特定的上下文。

  • 考虑捕获通配符的最佳方式(capture<? extends Foo>) 是作为unnamed相同边界的类型参数(即<T extends Foo>,但无法参考T).
  • “释放”捕获功能的最佳方法是将其绑定到泛型方法的命名类型参数。我将在下面的示例中演示这一点。请参阅 Java 教程“通配符捕获和辅助方法” http://docs.oracle.com/javase/tutorial/java/generics/capture.html(感谢@WChargin 的参考)以供进一步阅读。

假设我们想要一个方法来移动列表,换行到后面。然后假设我们的列表有一个未知(通配符)类型。

public static void main(String... args) {
    List<? extends String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    List<? extends String> cycledTwice = cycle(cycle(list));
}

public static <T> List<T> cycle(List<T> list) {
    list.add(list.remove(0));
    return list;
}

这很好用,因为T决心capture<? extends String>, not ? extends String。如果我们改为使用循环的非通用实现:

public static List<? extends String> cycle(List<? extends String> list) {
    list.add(list.remove(0));
    return list;
}

它将无法编译,因为我们没有通过将捕获分配给类型参数来使其可访问。

所以这开始解释为什么消费者singletonList将受益于类型推断器解析T to Test<capture<? extends Number>,从而返回一个List<Test<capture<? extends Number>>>代替List<Test<? extends Number>>.

但为什么一个不能分配给另一个呢?

为什么我们不能只分配一个List<Test<capture<? extends Number>>> to a List<Test<? extends Number>>?

好吧,如果我们考虑一下这个事实capture<? extends Number>相当于一个匿名类型参数,其上限为Number,那么我们可以把这个问题变成“为什么下面的代码不能编译?” (事实并非如此!):

public static <T extends Number> List<Test<? extends Number>> assign(List<Test<T>> t) {
    return t;
} 

这有充分的理由不编译。如果是的话,那么这将是可能的:

//all this would be valid
List<Test<Double>> doubleTests = null;
List<Test<? extends Number>> numberTests = assign(doubleTests);

Test<Integer> integerTest = null;
numberTests.add(integerTest); //type error, now doubleTests contains a Test<Integer>

那么为什么显性会起作用呢?

让我们回到开头。如果上述不安全,那么为什么允许这样做:

List<Test<? extends Number>> l =
    Collections.<Test<? extends Number>>singletonList(t);

为此,这意味着允许以下操作:

Test<capture<? extends Number>> capturedT;
Test<? extends Number> t = capturedT;

好吧,这不是有效的语法,因为我们无法显式引用捕获,所以让我们使用与上面相同的技术来评估它!让我们将捕获绑定到“分配”的不同变体:

public static <T extends Number> Test<? extends Number> assign(Test<T> t) {
    return t;
} 

这样就编译成功了。不难看出为什么它应该是安全的。这就是类似的用例

List<? extends Number> l = new List<Double>();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

泛型的泛型如何工作? 的相关文章

  • Hashmap并发问题

    我有一个哈希图 出于速度原因 我希望不需要锁定 假设我不介意过时的数据 同时更新它和访问它会导致任何问题吗 我的访问是获取 而不是迭代 删除是更新的一部分 是的 这会导致重大问题 一个例子是向散列映射添加值时可能发生的情况 这可能会导致表重
  • JavaFX 图像未在舞台中显示

    我尝试了很多次 尝试了很多方法 但都无法让自己的形象在舞台上如我所愿 我认为这可能与java寻找资源的路径有关 但我不确定 因为我刚刚开始使用视觉库 在本例中为JavaFX 这是我的目录结构 MyProject assets img myI
  • MongoTemplate upsert - 从 pojo 进行更新的简单方法(哪个用户已编辑)?

    这是一个简单的 pojo public class Description private String code private String name private String norwegian private String en
  • Google App Engine with Java - 运行 javac.exe 编译器时出错

    在 Windows XP 上 刚刚下载并解压谷歌应用程序引擎java sdk to C Program Files appengine java sdk 我已经安装了jdk C Program Files Java jdk1 6 0 20
  • 使用 Java 在 WebDriver 中按 Ctrl+F5 刷新浏览器

    我已经使用 java 刷新了 WebDriver 中的浏览器 代码如下 driver navigate refresh 如何使用 Java 在 WebDriver 中按 Ctrl F5 来做到这一点 我认为您可以使用 WebDriver 和
  • 解决 Java Checkstyle 错误:名称 'logger' 必须匹配模式 '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'

    使用 Eclipse Checkstyle 插件我看到以下错误 名称 logger 必须匹配模式 A Z A Z0 9 A Z0 9 我通过更改解决了此错误 private static final Logger logger Logger
  • 使用 ChannelExec 的命令未执行 - Jsch

    我正在使用 Jsch 在服务器中创建一个文件并执行一些命令 对于文件创建 它工作正常 但是对于命令执行 则不然 它保持状态 1 仍在处理它 并永远保持该状态 这种情况发生在 shell 执行或我尝试成为 root 时 请按照以下方法操作 p
  • 如何比较 Struts 2 中 url 请求参数中的单个字符

    我正在读取具有单个字符的 url 参数 它将是Y or N 我必须写一个条件来检查它是否Y or N并做相应的事情 这是我写的 但似乎不起作用 总是转到其他地方 网址是
  • 尝试使用 JRI 将 R 与我的 Java 应用程序集成,但出现错误。谁能解释一下原因和解决办法吗?

    我需要将 Java 与 R 集成来运行一些数学命令并使用 R 的功能进行绘图 以下部分代码给出了错误 public static void main String args HelloRWorld r new HelloRWorld r h
  • 定期更新 SWT 会导致 GUI 冻结

    Problem 当 GUI 字段定期更新时 SWT 会冻结 我想要一个基于 SWT 的 GUI 其中文本字段的值会定期递增 最初我从单独的线程访问 textField 导致抛出异常 线程 Thread 0 org eclipse swt S
  • Java Microsoft Excel API [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 发生错误。请参阅日志文件 - eclipse juno

    每当我启动 Eclipse Juno 时 都会出现错误 发生错误 请查看日志文件 C Program Files eclipse configuration 1362989254411 log 有的网站说卸载jdk重新安装 我这样做了 但没
  • 在 Java 中将弯音发送到 MIDI 音序器

    我了解启动和运行 MIDI 音序器的基础知识 并且希望能够在播放过程中增加 减小序列的音高 但弯音是发送到合成器而不是音序器的消息 我尝试将音序器的接收器设置为合成器的发射器 当我发送弯音短消息时 音序器保持相同的音调 但随后合成器以新的弯
  • 了解 Spark 中的 DAG

    问题是我有以下 DAG 我认为当需要洗牌时 火花将工作划分为不同的阶段 考虑阶段 0 和阶段 1 有些操作不需要洗牌 那么为什么 Spark 将它们分成不同的阶段呢 我认为跨分区的实际数据移动应该发生在第 2 阶段 因为这里我们需要cogr
  • 为什么这个私人浮动字段变为零?

    我有一些奇怪的行为 我很难向自己解释 称为 textureScale 的浮点字段变为零 如果某些代码正在更改该值 则可以解释这一点 然而 我希望能够通过将其设置为 私有最终浮点 来导致构建失败 或者至少是运行时异常 那么无论更改该值都将失败
  • init 中的 Swift 通用约束

    我有通用的 我希望能够用特定的约束来初始化它 约束仅用于初始化 班里的其他人并不关心 这是一个简化的示例 struct Generic
  • 失败时石英重试

    假设我有一个这样配置的触发器
  • 为什么应该首选 Java 类的接口?

    PMD https pmd github io 将举报以下违规行为 ArrayList list new ArrayList 违规行为是 避免使用 ArrayList 等实现类型 而是使用接口 以下行将纠正违规行为 List list ne
  • 为什么范围为“provided”的依赖项会隐藏 Maven 中的传递依赖项?

    我的 Maven 项目中有三个模块 这稍微简化了 model包含JPA注释的实体类 坚持实例化一个实体管理器并调用它的方法 应用创建类的实例model 设置一些值并将它们传递给坚持 model and 坚持显然取决于javax persis
  • 如何捕获 try-with-resource 语句中 close 方法抛出的异常

    我正在读关于try with resourceJava 中的语句可用于指定任意数量的资源 try Resource1 res1 initialize code Resource1 res2 initialize code statement

随机推荐