泛型通配符捕获和Helper方法
在某些情况下,编译器会推断出通配符的类型,例如,列表可以定义为List>,但是在评估表达式时,编译器会从代码中推断出特定类型,此场景称为通配符捕获。
在大多数情况下,你不必担心通配符捕获,除非你看到包含短语“capture of”的错误消息。
WildcardError示例在编译时产生捕获错误:
import java.util.List; public class WildcardError { void foo(List> i) { i.set(0, i.get(0)); } }
在此示例中,编译器将i输入参数处理为Object类型,当foo方法调用List.set(int, E)时,编译器无法确认插入到列表中的对象的类型,并且会产生错误,发生此类错误时,通常意味着编译器认为你为变量分配了错误的类型,出于这个原因,泛型被添加到Java语言中 — 在编译时强制执行类型安全。
由Oracle的JDK 7 javac实现编译时,WildcardError示例生成以下错误:
WildcardError.java:6: error: method set in interface Listcannot be applied to given types; i.set(0, i.get(0)); ^ required: int,CAP#1 found: int,Object reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion where E is a type-variable: E extends Object declared in interface List where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ? 1 error
在此示例中,代码尝试执行安全操作,那么如何解决编译器错误?你可以通过编写捕获通配符的私有Helper方法来修复它,在这种情况下,你可以通过创建私有Helper方法fooHelper来解决此问题,如WildcardFixed中所示:
public class WildcardFixed { void foo(List> i) { fooHelper(i); } // Helper method created so that the wildcard can be captured // through type inference. privatevoid fooHelper(List l) { l.set(0, l.get(0)); } }
由于Helper方法,编译器使用推断来确定T是调用中的CAP#1(捕获变量),该示例现在成功编译。
按照惯例,Helper方法通常命名为originalMethodNameHelper。
现在考虑一个更复杂的例子,WildcardErrorBad:
import java.util.List; public class WildcardErrorBad { void swapFirst(List extends Number> l1, List extends Number> l2) { Number temp = l1.get(0); l1.set(0, l2.get(0)); // expected a CAP#1 extends Number, // got a CAP#2 extends Number; // same bound, but different types l2.set(0, temp); // expected a CAP#1 extends Number, // got a Number } }
在这个例子中,代码正在尝试不安全的操作,例如,考虑以下对swapFirst方法的调用:
Listli = Arrays.asList(1, 2, 3); List ld = Arrays.asList(10.10, 20.20, 30.30); swapFirst(li, ld);
List
使用Oracle的JDK javac编译器编译代码会产生以下错误:
WildcardErrorBad.java:7: error: method set in interface Listcannot be applied to given types; l1.set(0, l2.get(0)); // expected a CAP#1 extends Number, ^ required: int,CAP#1 found: int,Number reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion where E is a type-variable: E extends Object declared in interface List where CAP#1 is a fresh type-variable: CAP#1 extends Number from capture of ? extends Number WildcardErrorBad.java:10: error: method set in interface List cannot be applied to given types; l2.set(0, temp); // expected a CAP#1 extends Number, ^ required: int,CAP#1 found: int,Number reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion where E is a type-variable: E extends Object declared in interface List where CAP#1 is a fresh type-variable: CAP#1 extends Number from capture of ? extends Number WildcardErrorBad.java:15: error: method set in interface List cannot be applied to given types; i.set(0, i.get(0)); ^ required: int,CAP#1 found: int,Object reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion where E is a type-variable: E extends Object declared in interface List where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ? 3 errors
这里没有Helper方法来解决这个问题,因为代码根本就是错误的。
上一篇:泛型通配符文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/72942.html
Java™ 教程 Java教程是为JDK 8编写的,本页面中描述的示例和实践没有利用在后续版本中引入的改进。 Java教程是希望使用Java编程语言创建应用程序的程序员的实用指南,其中包括数百个完整的工作示例和数十个课程,相关课程组被组织成教程。 覆盖基础知识的路径 这些教程以书籍的形式提供,如Java教程,第六版,前往Amazon.com购买。 入门 介绍Java技术和安装Java开发软件并使用...
泛型的限制 要有效地使用Java泛型,必须考虑以下限制: 无法使用基元类型实例化泛型类型 无法创建类型参数的实例 无法声明类型为类型参数的静态字段 无法对参数化类型使用强制类型转换或instanceof 无法创建参数化类型的数组 无法创建、捕获或抛出参数化类型的对象 无法重载将每个重载的形式参数类型擦除为相同原始类型的方法 无法使用基元类型实例化泛型类型 考虑以下参数化类型: class P...
泛型通配符 在泛型代码中,称为通配符的问号(?)表示未知类型,通配符可用于各种情况:作为参数、字段或局部变量的类型,有时作为返回类型(尽管更好的编程实践是更加具体),通配符从不用作泛型方法调用、泛型类实例创建或超类型的类型参数。 以下部分更详细地讨论通配符,包括上界通配符、下界通配符和通配符捕获。 上界通配符 你可以使用上界通配符来放宽对变量的限制,例如,假设你要编写一个适用于List、List和...
摘要:好了,有了这样的背景知识,我们可以来看一下上界通配了,在中,可以使用来界定一个上界,的意思是所有属于的子类,是上界,不能突破天界啊,我们具体化一下,的意思就是,所有的子类都可以匹配这个通配符。 1、上界通配符 首先,需要知道的是,Java语言中的数组是支付协变的,什么意思呢?看下面的代码: static class A extends Base{ void f(...
摘要:静态变量是被泛型类的所有实例所共享的。所以引用能完成泛型类型的检查。对于这个类型系统,有如下的一些规则相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。事实上,泛型类扩展都不合法。 前言 和C++以模板来实现静多态不同,Java基于运行时支持选择了泛型,两者的实现原理大相庭径。C++可以支持基本类型作为模板参数,Java却只能接受类作为泛型参数;Java可以在泛型类的方法中取得...
阅读 2103·2021-09-04 16:40
阅读 1402·2021-08-13 15:07
阅读 3569·2019-08-30 15:53
阅读 3166·2019-08-30 13:11
阅读 1019·2019-08-29 17:22
阅读 1748·2019-08-29 12:47
阅读 1437·2019-08-29 11:27
阅读 2188·2019-08-26 18:42