Java 嵌套泛型类型

2023-11-24

为什么必须使用泛型类型Map<?, ? extends List<?>>而不是更简单的Map<?, List<?>>对于以下test() method?

public static void main(String[] args) {
    Map<Integer, List<String>> mappy =
        new HashMap<Integer, List<String>>();

    test(mappy);
}

public static void test(Map<?, ? extends List<?>> m) {}

// Doesn't compile
// public static void test(Map<?, List<?>> m) {}

请注意以下方法有效,并且这三种方法无论如何都具有相同的擦除类型。

public static <E> void test(Map<?, List<E>> m) {}

从根本上来说,List<List<?>> and List<? extends List<?>>有不同的类型参数。

实际上,一种类型是另一种类型的子类型,但首先让我们详细了解它们各自的含义。

理解语义差异

一般来说,通配符?代表一些“缺失的信息”。它的意思是“这里曾经有一个类型参数,但我们不再知道它是什么了”。而且因为我们不知道它是什么,所以我们对如何使用引用该特定类型参数的任何内容施加了限制。

目前,让我们使用以下方法来简化示例List代替Map.

  • A List<List<?>> holds 具有任何类型参数的任何类型的列表。所以即:

    List<List<?>> theAnyList = new ArrayList<List<?>>();
    
    // we can do this
    theAnyList.add( new ArrayList<String>() );
    theAnyList.add( new LinkedList<Integer>() );
    
    List<?> typeInfoLost = theAnyList.get(0);
    // but we are prevented from doing this
    typeInfoLost.add( new Integer(1) );
    

    我们可以把任何List in theAnyList,但这样做我们就失去了关于他们的元素.

  • 当我们使用? extends, the List holds List 的某些特定子类型,但我们不再知道它是什么。所以即:

    List<? extends List<Float>> theNotSureList =
        new ArrayList<ArrayList<Float>>();
    
    // we can still use its elements
    // because we know they store Float
    List<Float> aFloatList = theNotSureList.get(0);
    aFloatList.add( new Float(1.0f) );
    
    // but we are prevented from doing this
    theNotSureList.add( new LinkedList<Float>() );
    

    向其中添加任何内容不再安全theNotSureList,因为我们不知道其元素的实际类型。 (Was它原本是一个List<LinkedList<Float>>? Or a List<Vector<Float>>?我们不知道。)

  • 我们可以把它们放在一起并得到一个List<? extends List<?>>。我们不知道什么类型List它已经存在了,而且我们不知道它的元素类型those List要么。所以即:

    List<? extends List<?>> theReallyNotSureList;
    
    // these are fine
    theReallyNotSureList = theAnyList;
    theReallyNotSureList = theNotSureList;
    
    // but we are prevented from doing this
    theReallyNotSureList.add( new Vector<Float>() );
    // as well as this
    theReallyNotSureList.get(0).add( "a String" );
    

    我们丢失了信息both about theReallyNotSureList, 的元素类型List就在里面。

    (但您可能会注意到,我们可以assign任何一种持有清单列表对它...)

所以要分解它:

//   ┌ applies to the "outer" List
//   ▼
List<? extends List<?>>
//                  ▲
//                  └ applies to the "inner" List

The Map工作方式相同,只是有更多类型参数:

//  ┌ Map K argument
//  │  ┌ Map V argument
//  ▼  ▼
Map<?, ? extends List<?>>
//                    ▲
//                    └ List E argument

Why ? extends是必要的

你可能知道“具体的”泛型类型有不变性, 那是,List<Dog>不是 的子类型List<Animal>即使class Dog extends Animal。相反,通配符就是我们的方式协方差, 那是,List<Dog> is的一个子类型List<? extends Animal>.

// Dog is a subtype of Animal
class Animal {}
class Dog extends Animal {}

// List<Dog> is a subtype of List<? extends Animal>
List<? extends Animal> a = new ArrayList<Dog>();

// all parameterized Lists are subtypes of List<?>
List<?> b = a;

因此,将这些想法应用到嵌套中List:

  • List<String>是一个子类型List<?> but List<List<String>> is not的一个子类型List<List<?>>。如前所示,这可以防止我们通过向类型添加错误的元素来损害类型安全。List.
  • List<List<String>> is的一个子类型List<? extends List<?>>,因为有界通配符允许协方差。那是,? extends允许这样的事实List<String>是一个子类型List<?>予以考虑。
  • List<? extends List<?>>实际上是一个共享的超类型:

         List<? extends List<?>>
              ╱          ╲
    List<List<?>>    List<List<String>>
    

审核中

  1. Map<Integer, List<String>>接受only List<String>作为一个值。
  2. Map<?, List<?>>接受any List作为一个值。
  3. Map<Integer, List<String>> and Map<?, List<?>>是具有不同语义的不同类型。
  4. 一个不能转换为另一个,以防止我们以不安全的方式进行修改。
  5. Map<?, ? extends List<?>>是一个施加安全限制的共享超类型:

            Map<?, ? extends List<?>>
                 ╱          ╲
    Map<?, List<?>>     Map<Integer, List<String>>
    

通用方法如何工作

通过在方法上使用类型参数,我们可以断言List有一些具体的类型。

static <E> void test(Map<?, List<E>> m) {}

该特定声明要求all List是在Map具有相同的元素类型。我们不知道该类型实际上是什么is,但我们可以以抽象的方式使用它。这使得我们可以进行“盲”操作。

例如,这种声明可能对某种累积有用:

static <E> List<E> test(Map<?, List<E>> m) {
    List<E> result = new ArrayList<E>();

    for(List<E> value : m.values()) {
        result.addAll(value);
    }

    return result;
}

我们不能打电话put on m因为我们不知道它是什么key type不再是了。然而,我们可以操纵它values因为我们知道他们都是List与相同的元素类型.

只是为了踢球

该问题未讨论的另一个选项是同时具有有界通配符和泛型类型List:

static <E> void test(Map<?, ? extends List<E>> m) {}

我们可以用类似的东西来调用它Map<Integer, ArrayList<String>>。如果我们只关心类型,这是最宽松的声明E.

我们还可以使用边界来嵌套类型参数:

static <K, E, L extends List<E>> void(Map<K, L> m) {
    for(K key : m.keySet()) {
        L list = m.get(key);
        for(E element : list) {
            // ...
        }
    }
}

这既允许我们传递给它什么,也允许我们如何操纵m以及其中的一切。


See also

  • “Java 泛型:什么是 PECS?”对于之间的差异? extends and ? super.
  • JLS 4.10.2。类和接口类型之间的子类型化 and JLS 4.5.1。参数化类型的类型参数获取此答案的技术细节的切入点。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 嵌套泛型类型 的相关文章

  • 在Java Servlet中获取通过jquery ajax发送的参数[重复]

    这个问题在这里已经有答案了 我在网上搜索这个主题 但找不到有效的示例 我会很高兴有人能给我帮助 这就是我测试的 ajax url GetJson type POST dataType json contentType application
  • 来自行号的方法名称

    给定特定类源代码 Java C 的行号 是否有一种简单的方法来获取它所属的方法的名称 如果它落入其中 大概使用抽象语法树 这对于将 checkstyle 的输出限制为仅触及的方法很有用 我假设您必须使用抽象语法树来执行 Line gt Me
  • IBM Websphere MQ - 用于 Tomcat 部署的 EJB 和 MDB 迁移

    我已经为此苦苦挣扎了很长一段时间 我有一个 IBM Websphere MQ 它使用 EJB 和 MDB 以下是配置ejb mdb的地方
  • 如何重复一段文本中的每个字母?爪哇语

    就像在口吃中一样 如果文本为 dean 并且乘数为 3 则结果将是 dddeeeaaannn 由提供的乘数指定的次数 public static void repeatLetters String text dean int n 3 Str
  • java中高效的输入流到字符串方法

    因此 我在 Java 中的 诚然非常简单 应用程序上运行探查器 令我惊讶的是 仅次于需要在时间上发出 HTTP 请求的方法的是我的方法 inputStreamToString方法 目前它的定义如下 public static String
  • 使用 Spring MVC 在 jar 文件中显示 jsp 页面

    我正在使用 Spring MVC 3 2 2 在 java 中开发一个 Web 应用程序 我在从 jar 文件中加载 jsp 页面时遇到问题 Spring MVC Web应用程序具有以下结构 META INF WEB INF spring
  • 当前平台不支持桌面 API

    我遇到过这个错误 java lang UnsupportedOperationException 当前平台不支持桌面 API 我将从我的 java 应用程序中打开一个文件 我用这个方法 Desktop getDesktop open new
  • 用于制作代码编辑器的 JavaFX 相当于 JSyntaxPane 的什么?

    以前在 Swing 中 我使用过JSyntaxPane用于制作一个小型 Java 源代码编辑器 为了练习 我决定用 JavaFX 重做整个项目并添加对更多语言的支持 最好是尽可能多 不过好像没有什么类似的JSyntaxPane 一些研究让我
  • 如何将一个 SwiftUI View 作为变量传递给另一个 View 结构

    我正在实施一个very自定义 NavigationLink 称为MenuItem并希望在整个项目中重用它 它是一个符合以下条件的结构体View并实施var body some View其中包含一个NavigationLink 我需要以某种方
  • 在 Eclipse 中删除空块之前的新行

    我更喜欢奥尔曼式 http en wikipedia org wiki Brace style Allman style大括号 例如 if foo magical prancing unicorn stuff 而不是 if foo unma
  • java.exe 以非零退出值 1 结束

    只是为了开始 我并不是真正尝试从 Android 中的 xlsx 文件中读取单元格 我已经尝试了几乎所有我在 Google 上搜索到的内容 但是每次 在两台不同的 PC 上 都是 Java 1 7 0 79 当我尝试构建 运行 这个应用程序
  • BigDecimal汇总统计

    我有一个 BigDecimal 列表 List
  • 如何在 JmsMessagingTemplate.sendAndReceive 上设置等待超时

    我在 MVC 控制器中使用 JmsMessagingTemplate 的 sendAndReceive 但如果没有发送回复消息 它似乎会永远等待回复 该文档指出 返回 回复 如果无法接收消息 例如由于超时 则可能为 null 然而 我只是不
  • 为 REST API 生成 Swagger UI 文档

    我使用 Java 中的 JAX RS Jersey 开发了 REST API 我想为其转换 生成基于 Swagger 的 UI 文档 谁能以简单的方式告诉我如何做到这一点的精确 步骤 很抱歉 他们网站上给出的步骤对我来说有点模糊 有多种方法
  • Hybris:如何在impex中导入zip文件中的媒体?

    我知道我们可以导入未像这样压缩的图像 siteResource jar com project initialdata constants ProjectInitialDataConstants projectinitialdata imp
  • 添加 char 和 int

    据我了解 字符是一个字符 即一个字母 一个digit 标点符号 制表符 空格或类似的东西 因此 当我这样做时 char c 1 System out println c 输出 1 正是我所期望的 那么为什么当我这样做时 int a 1 ch
  • 在 Tensorflow-lite Android 中将位图转换为 ByteBuffer(浮点)

    在用于图像分类的tensorflow lite android演示代码中 图像首先转换为ByteBuffer格式以获得更好的性能 这种从位图到浮点格式的转换以及随后到字节缓冲区的转换似乎是一个昂贵的操作 循环 按位运算符 float mem
  • 如何在不使用 -cp 开关的情况下在 Groovy 中自动加载数据库 jar?

    我想简化调用 Oracle 数据库的 Groovy 脚本的执行 如何将 ojdbc jar 添加到默认类路径以便我可以运行 groovy RunScript groovy 代替 groovy cp ojdbc5 jar RunScript
  • Spring MVC:通用 DAO 和服务类

    我正在 Spring MVC 中编写网页 我使用 Generic DAO 编写了所有 DAO 现在我想重写我的服务类 我该如何写 通用服务 我的 DAO 如下 DAO package net example com dao import j
  • 用于生成 ISO 文件的 Maven 插件

    有没有可以生成ISO镜像的maven插件 我需要获取一些模块的输出 主要是包含 jar 的 zip 文件 并将它们组合成一个 ISO 映像 Thanks 现在有一个 ISO9660 maven 插件可以完成这项工作 https github

随机推荐

  • xdebug断点失败

    以通常的方式庆祝建立一个新的测试服务器 通过寻求帮助让 xdebug 工作 Server Ubuntu 服务器 16 10 Nginx PHP 7 Xdebug 设置的要求如下http php built com installing xd
  • 在水豚中选择具有多个类的元素

    我正在使用 Selenium 在 Capybara 中编写自动化代码 我的 HTML 中有以下元素 我想在水豚中单击该元素 a href class classA classB click me a 目前 工作方式如下 find class
  • 多重继承会导致虚假的、不明确的虚函数重载

    在这个例子中 类Foo and Bar由图书馆提供 我的课Baz继承两者 struct Foo void do stuff int int struct Bar virtual void do stuff float 0 struct Ba
  • .Net 的开源代理库 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 我正在寻找一个开源的 Ne
  • 将 []byte 转换为小/大端有符号整数或浮点数?

    我能够转换 byte转换为无符号整数 a binary LittleEndian Uint16 sampleA b binary BigEndian Uint32 sampleB 这利用了 Go 包中的 Big Endian 和 Littl
  • 我应该关闭JNDI获取的数据源吗?

    更新 显然 Tomcat 从 7 0 11 开始 为您关闭了数据源 因此它在 web 应用程序的 contextDestroyed 中不可用 看 https issues apache org bugzilla show bug cgi i
  • objdump - head ELF - 标志的含义?

    objdump f a out a out file format elf32 i386 architecture i386 flags 0x00000112 EXEC P HAS SYMS D PAGED start address 0x
  • C# 中的流重用

    我一直在尝试一个我认为很简单的想法 我希望能够从某个地方 网站 文件系统 ftp 读取文件 对其执行一些操作 压缩 加密等 然后将其保存在某个地方 可能是文件系统 ftp 或其他任何地方 这是基本的管道设计 我想做的就是读入文件并将其放入
  • 仅通过存储过程强制 INSERT

    使用 SQL Server 2008 是否有一种方法允许仅通过存储过程插入表 如果可以 如何实现 EDIT 最好的方法可能是马丁 史密斯的建议 即使用代替插入扳机 这个问题的直接答案是 marc s 的 GRANT 和 DENY 答案 尽管
  • java中删除和重命名文件

    我在java中创建了一个文件 file1 我读取了该 file1 并对从 file1 读取的数据进行了一些更改 然后将新数据写入另一个文件 file2 现在我需要的是删除以前的文件 file1 并将文件 file2 的名称更改为 file1
  • 创建动态 html 表单

    我想创建一个动态变化的表单 我有一个用于创建项目的表单 包含以下字段 project name project description 并且该项目可以具有任意数量 大于或等于 0 的类别 我想要的是显示一个按钮 让用户可以选择添加另一个类别
  • 删除 Lucene.net 中的所有索引

    我想删除all之前创建的索引 我在用Lucene net 我尝试了以下方法 Term term new Term empty because I want to delete all the indices IndexReader rdr
  • 仅当字符为 Firebird 2.5 上的数字时才转换为 Int

    我有一个在 MySQL 数据库上使用的查询 该查询对结果进行排序 在可能的情况下 当 char 字符串是数字时 将 char 数据库字段转换为整数 因此例如ORDER BY我在 MySQL 上使用的子句是 ORDER BY CASE WHE
  • 如何在刷新页面时抑制重新发布 - ASP.NET MVC

    我正在使用 asp net mvc 构建一个向导 目前 当用户点击下一个 或上一个 时 表单值将被发布到一个操作 该操作执行所需的任何处理 然后呈现下一个视图 我遇到的问题是 如果用户在新视图中点击刷新 他们会被提示重新发布表单值 这会导致
  • 兼容 x86 的英特尔至强融核加速器中是否有 SIMD(SSE / AVX) 指令?

    x86 兼容加速器 MIC Intel Xeon Phi 中是否有 SIMD SSE AVX 指令 http en wikipedia org wiki Xeon Phi 是的 最新一代的英特尔至强融核协处理器 代号 骑士角 缩写 KNC
  • 如何使用伪 CGO 指令在子目录中添加 C 文件作为 go build 的一部分?

    根据文档 go buildwith cgo 将在包的根目录中添加任何 C C 文件作为编译的一部分 有没有办法使用 CGO 指令使给定子目录中的 C C 文件以及根目录中的文件也成为编译的一部分 并不真地 您唯一的选择是将子目录设为另一个
  • Flask 路由模式匹配顺序

    鉴于Flask 路由不是从上到下进行模式匹配的 如何处理以下问题 我有以下路线
  • 如何对并行 numpy 数组进行“压缩排序”?

    如果我有两个并行列表并想按第一个列表中元素的顺序对它们进行排序 这非常简单 gt gt gt a 2 3 1 gt gt gt b 4 6 7 gt gt gt a b zip sorted zip a b gt gt gt print a
  • 从 jQuery 调用 WCF 服务库时出现问题

    我通过我的 ASPX 站点公开了一个 WCF 服务库 如下所示 System ServiceModel OperationContract System ServiceModel Web WebInvoke Method POST Requ
  • Java 嵌套泛型类型

    为什么必须使用泛型类型Map gt m Doesn t compile public static void test Map gt m 请注意以下方法