限定通配符和非限定通配符_为什么我不信任通配符以及为什么我们仍然需要通配符...

限定通配符和非限定通配符

在将子类型多态性(面向对象)与参数多态性(泛型)相结合的任何编程语言中,都会出现方差问题。 假设我有一个字符串列表,键入List<String> 。 我可以将其传递给接受List<Object>的函数吗? 让我们从这个定义开始:



interface List<T> {void add(T element);Iterator<T> iterator();...
}

破碎的协方差

凭直觉,我们可能首先认为应该允许这样做。 看起来不错:

void iterate(List<Object> list) {Iterator<Object> it = list.iterator();...
}
iterate(ArrayList<String>());

确实,包括Eiffel和Dart在内的某些语言确实接受此代码。 可悲的是,它是不完善的,如以下示例所示:

//Eiffel/Dart-like language with
//broken covariance:
void put(List<Object> list) {list.add(10);
}
put(ArrayList<String>());

在这里,我们将List<String>传递给接受List<Object>的函数,该函数尝试将Integer添加到列表中。

Java对数组也会犯同样的错误。 以下代码编译:

//Java:
void put(Object[] list) {list[0]=10;
}
put(new String[1]);

它在运行时失败,并带有ArrayStoreException

使用地点差异

但是,对于通用类和接口类型,Java采用了不同的方法。 默认情况下,类或接口类型为invariant ,即:

  • 当且仅当UV类型完全相同时,才可将L<V>分配给L<V>

由于这在很多时候非常不方便,因此Java支持一种称为use-sitevariance的方法 ,其中:

  • L<U>可分配给L<? extends V> 如果UV的子类型,则L<? extends V> ,并且
  • L<U>可分配给L<? super V> L<? super V>如果U是的超类型V

丑陋的语法? extends V ? extends V? super V ? super V称为通配符 。 我们还说:

  • L<? extends V> L<? extends V>V协变的,并且
  • L<? super V> L<? super V>V反变的。

由于Java的通配符表示法非常丑陋,因此在本讨论中我们将不再使用它。 取而代之的是,我们将分别使用关键字inout来表示通变量和协方差。 从而:

  • L<out V>V协变的,并且
  • L<in V>是在逆变 V

给定的V称为通配符的边界

  • out V是一个上限通配符, V是其上限,并且
  • in V下界通配符, V是其下界。

从理论上讲,我们可以有一个具有上限和下限的通配符,例如L<out X in Y>
我们可以使用交集类型表示多个上限或多个下限,例如L<out U&V>L<in U&V>
请注意,类型表达式L<out Anything>L<in Nothing>指的是完全相同的类型,并且此类型是L的所有实例的超类型。 您会经常看到人们将通配符类型称为存在性类型 。 他们的意思是,如果我知道list的类型为List<out Object>

List<out Object> list;

然后我知道存在一个未知的类型T ,这是Object的子类型,因此list的类型为List<T>
或者,我们可以从更宽泛的角度出发,说List<out Object>是所有List<T>类型的并集,其中TObject的子类型。
在具有使用地点差异的系统中,以下代码无法编译:

void iterate(List<Object> list) {Iterator<Object> it = list.iterator();...
}
iterate(ArrayList<String>()); //error: List<String> not a List<Object>

但是这段代码可以做到:

void iterate(List<out Object> list) {Iterator<out Object> it = list.iterator();...
}
iterate(ArrayList<String>());

正确地,此代码无法编译:

void put(List<out Object> list) {list.add(10); //error: Integer is not a Nothing
}
put(ArrayList<String>());

现在我们在兔子洞的入口。 为了将通配符类型集成到类型系统中,同时像上面的示例一样拒绝不正确的代码,我们需要一种更为复杂的算法来替换类型实参。

会员输入使用地点差异

也就是说,当我们有一个泛型类型类似List<T>有一种方法void add(T element) ,而不是仅仅直截了当代ObjectT ,就像我们做普通不变的类型,我们需要考虑的方差类型参数出现的位置。 在这种情况下, T出现在List类型的反位置 ,即作为方法参数的类型。 我不会在这里写下的复杂算法告诉我们,在此位置我们应该用Nothing (底部类型)代替。
现在想象一下我们的List接口有一个带有以下签名的partition()方法:

interface List<T> {List<List<T>> partition(Integer length);...
}

List<out Y>partition()的返回类型是什么? 好吧,在不损失精度的情况下,它是:

List<in List<in Y out Nothing> out List<in Nothing out Y>>

哎哟。
由于没有人在他们的头脑中想去考虑这样的类型,因此明智的语言会抛弃其中的一些界限,而留下这样的东西:

List<out List<out Y>>

这是可以接受的。 不幸的是,即使在这种非常简单的情况下,我们也已经远远超出了程序员可以轻松跟随类型检查器所做的工作的地步。
因此,这就是我不信任使用地点差异的原因所在:

  • Ceylon设计的一个重要原则是,程序员应始终能够重现编译器的推理。 这是原因的一些与使用现场方差出现的复杂类型的非常困难。
  • 它具有病毒性作用:一旦这些通配符类型在代码中立足,它们便开始传播,很难回到我的普通不变式类型。

申报地点差异

使用场所方差的一种更合理的选择是声明场所方差 ,在声明时我们指定泛型类型的方差。 这是我们在锡兰使用的系统。 在此系统下,我们需要将List分为三个接口:

interface List<out T> {Iterator<T> iterator();List<List<T>> partition(Integer length);...
}interface ListMutator<in T> {void add(T element);
}interface MutableList<T>satisfies List<T>&ListMutator<T> {}

List声明为协变类型, ListMutator声明为协变类型, ListMutator声明为MutableList的不变子类型。
似乎对多个接口的需求似乎是声明站点差异的一个很大的缺点,但事实证明,将突变与读取操作分开是很有用的,并且:

  • 变异运算通常是不变的,而
  • 读取操作通常是协变的。

现在我们可以这样编写函数:

void iterate(List<Object> list) {Iterator<Object> it = list.iterator();...
}
iterate(ArrayList<String>());void put(ListMutator<Integer> list) {list.add(10);
}
put(ArrayList<String>()); //error: List<String> is not a ListMutator<Integer>

您可以在此处阅读有关声明位置差异的更多信息。

为什么我们在锡兰需要使用场所差异

可悲的是,Java没有声明站点差异,并且与Java的良好互操作对我们来说非常重要。 我不喜欢纯粹为了与Java互操作而在语言的类型系统中添加主要功能,因此多年来,我一直拒绝向Ceylon添加通配符。 最后,现实和实用性获胜,而我的顽固失去了。 因此,Ceylon 1.1现在具有带有单界通配符的使用站点差异。
我试图尽可能严格地限制此功能,仅提供体面的Java互操作所需的最低限度的功能。 这意味着,就像在Java中一样:

  • 没有形式为List<in X out Y>双界通配符,并且
  • 在类或接口定义的extendssatisfies子句中不能出现通配符类型。

此外,与Java不同:

  • 没有隐式界通配符,上限必须始终以显式形式编写,并且
  • 不支持通配符捕获

通配符捕获是Java的一个非常聪明的功能,它利用了通配符类型的“现有”解释。 给定这样的通用函数:

List<T> unmodifiableList<T>(List<T> list) => ... :

Java让我调用unmodifiableList() ,传递一个通配符类型,如List<out Object> ,返回另一个通配符List<out Object> ,原因是存在一些未知的X ,这是Object的子类型,对其进行调用是正确的。 也就是说,即使无法为任何TList<out Object>类型分配给List<T> ,也认为该代码是正确的:

List<out Object> objects = .... ;
List<out Object> unmodifiable = unmodifiableList(objects);

在Java中,涉及通配符捕获的键入错误几乎是无法理解的,因为它们涉及未知且难以理解的类型。 我没有计划向锡兰添加对通配符捕获的支持。

试试看

使用站点差异已经实现,并且已经在Ceylon 1.1中起作用,如果您非常有动力,可以从GitHub获得。
即使此功能的主要动机是出色的Java互操作性,但在其他情况(可能很少见)中,通配符将很有用。 但是,这并不表示我们的方法有任何重大变化。 除极端情况外,我们将继续在Ceylon SDK中使用声明站点差异。
更新: 我只是意识到我忘了感谢Ross Tate的帮助,帮助我更好地了解了成员打字算法中使用地点差异的问题。 罗斯知道这些非常棘手的东西!

翻译自: https://www.javacodegeeks.com/2014/08/why-i-distrust-wildcards-and-why-we-need-them-anyway.html

限定通配符和非限定通配符

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340457.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php strtotime month bug,处理PHP strtotime的BUG

PHP strtotime的BUG处理最近使用了strtotime结合-1 month, 1 month, next month获取上个月或者下个月的日期&#xff0c;不过刚看到一篇文章&#xff0c;才知道原来使用strtotime直接获取日期还是有点小BUGBUG如日期&#xff1a;$today 2020-12-31;echo date("Y-m-d"…

JMetro版本11.5.11和8.5.11发布

你好 具有新JMetro样式的另一个版本&#xff08;深色和浅色版本&#xff09;&#xff1a; 分割菜单按钮 分割窗格 药丸按钮/分段按钮 调整现有样式和错误修复。 继续阅读以获取详细信息。 分割菜单按钮 以下是一个动画&#xff0c;显示了新的“拆分菜单按钮” JMetro浅色…

python远程连接mysql数据库_MySQL数据库之python mysql远程连接

本文主要向大家介绍了MySQL数据库之python mysql远程连接 &#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习MySQL数据库有所帮助。第一步&#xff1a;vim /etc/MySQL/my.cnf找到bind-address 127.0.0.1注释掉这行&#xff0c;如&#xff1a;#bind-address 12…

php100并发cpu告警,多线程并发导致CPU100%的一种原因和解决办法

在用自定义线程池的时候&#xff0c;遇到cpu100%&#xff0c;经过验证后&#xff0c;发现问题来源于我定义的子线程。子线程的主要功能是从任务队列(LinkedBlockingQueue)里面持续拿出任务&#xff0c;并且执行。以下为令CPU100的代码。private class WorkThread extends Threa…

excel离散度图表怎么算_一般人不知道的几个excel制图技巧

原标题&#xff1a;一般人不知道的几个excel制图技巧作者&#xff1a;杜雨 公众号&#xff1a;数据小魔方(datamofang)今天这篇&#xff0c;我专注于Excel的作图规则&#xff0c;深入的研究下Excel由数据源到可视化图表之间的关系是如何对应的&#xff0c;倘若你已经在工作中横…

listview控件在php的使用方法,Android_Android编程之控件ListView使用方法,本文实例讲述了Android编程之控 - phpStudy...

Android编程之控件ListView使用方法本文实例讲述了Android编程之控件ListView使用方法。分享给大家供大家参考。具体分析如下&#xff1a;控件ListView是一个重要的控件&#xff0c;可以被用作用户列表等显示&#xff0c;下面进行它的操作测试。下面代码实现了生成了一个ListVi…

使用ElasticSearch进行近实时索引

选择索引策略很困难。 Elasticsearch 文档的确有一些一般性建议 &#xff0c;并且有其他公司的 一些技巧 &#xff0c;但这也取决于特定的用例。 在典型情况下&#xff0c;您有一个数据库作为事实的来源&#xff0c;并且有一个使事物可搜索的索引。 您可以采用以下策略&#xf…

air什么意思中文_Air 的中文意思是什么?

展开全部air释义&#xff1a;(1)作为名词&#xff0c;空气&#xff1b;32313133353236313431303231363533e59b9ee7ad9431333431366261天空&#xff1b;(飞行的)空中。(2)作为动词&#xff0c;晾&#xff1b;晾干&#xff1b;(使)通风&#xff1b;公开发表。读音&#xff1a;英[…

python xlutils函数,python3:xlrd、xlwt、xlutils处理excel文件

1.xlrd读取excel请参考上篇博客https://www.cnblogs.com/shapeL/p/9075843.html2.xlwt生成excel安装下载&#xff1a;pip install xlwt导入&#xff1a;import xlwt参考&#xff1a;生成excel文件test1.xlsfile_name ../dataconfig/test1.xlswbk xlwt.Workbook() #初始化work…

前端实现炫酷动效_20个网页动效设计的炫酷神器

如今很多 UI 设计师不是正在做动效&#xff0c;就正在学着做动效。动效现在已经无处不在了。有的动效可能是一个微妙的悬停效果&#xff0c;使用 Bttn.css 或者是 Hover Buttons 的帮助下&#xff0c;添加到按钮上&#xff0c;有的则可能是使用引人瞩目的动态渐变背景抓住用户的…

php7设置最大连接数,sybase 15.7 修改 number of user connections 最大连接数

sp_configure number of user connectionssp_configure number of user connections,200修改Sybase最大连接数的方法有两种&#xff0c;如下&#xff1a;一、 进Sybase central, 鼠标右键选择数据库服务器(要处理的服务器)&#xff0c;然后选择右键菜单中的配置选项&#xff0c…

jsr303 自定义消息_JSR 303从I18N属性文件加载消息

jsr303 自定义消息总览 本文将说明如何适应JSR 303验证API来从I18n属性文件加载消息&#xff0c;并通过保留国际化的所有好处和对多种语言的支持来实现这一点。 为此&#xff0c;我们将实现一个基于Spring API的自定义MessageInterpolator&#xff0c;用于管理I18N消息。 依存…

最优隐神经元数目 算法 matlab,BP算法的改进在Matlab的实现研究

BP 算法的改进在M a tlab 的实现研究姚文俊(中南民族大学电子信息工程学院 湖北武汉 430074)摘 要:利用M atlab 中的神经网络工具箱提供的丰富网络学习和训练函数,对BP 网络和BP 算法的优化方案进行仿真,得到较优的BP 算法。关键词:人工神经网络;BP 网络;M atlab ;N eu ral …

php 导出excel分段导出_PHP 导出excel 数据量大时

public functionceshiexcel1(){set_time_limit(0);$filename 病毒日志;header(Content-Type: application/vnd.ms-excel);header(Content-Disposition: attachment;filename".$filename..csv");header(Cache-Control: max-age0);//原生链接mysql//数据库配置$mysql_…

通过Apache Kafka集成流式传输大数据

从实时实时过滤和处理大量数据&#xff0c;到将日志数据和度量数据记录到不同来源的集中处理程序中&#xff0c;Apache Kafka日益集成到各种系统和解决方案中。 使用CData Sync &#xff0c;可以轻松地将此类解决方案应用于任何CRM&#xff0c;ERP或Analytics软件。 配置Apach…

php7 获取数据流,stream_socket_accept()

stream_socket_accept()(PHP 5, PHP 7)接受由stream_socket_server()创建的套接字连接说明stream_socket_accept(resource$server_socket[,float$timeout ini_get("default_socket_timeout")[,string&$peername]]):resource接受由stream_socket_server()创建的套…

浪潮as5300技术方案_浪潮智能存储AS5300G2双活解决方案

智慧医疗已成为提高就医效率、减少医疗差错的有效科技手段。江西省泰和中医院通过采用浪潮智能存储AS5300G2双活解决方案&#xff0c;构筑了一个满足医院数字化、信息化、智能化、可持续发展的信息支撑系统平台&#xff0c;使业务数据有保障、资源调配共享更高效、运维管理更方…

安卓php推送消息机制,深入剖析Android消息机制原理

在Android中&#xff0c;线程内部或者线程之间进行信息交互时经常会使用消息&#xff0c;这些基础的东西如果我们熟悉其内部的原理&#xff0c;将会使我们容易、更好地架构系统&#xff0c;避免一些低级的错误。在学习Android中消息机制之前&#xff0c;我们先了解与消息有关的…

python单元测试mock_Mock 在 Python 单元测试中的使用

本文讲述的是 Python 中 Mock 的使用。如何执行单元测试而不用考验你的耐心很多时候&#xff0c;我们编写的软件会直接与那些被标记为“垃圾”的服务交互。用外行人的话说&#xff1a;服务对我们的应用程序很重要&#xff0c;但是我们想要的是交互&#xff0c;而不是那些不想要…

php bin2hex(),PHP bin2hex() 函数 pack() 函数

bin2hex() 函数bin2hex() 函数把 ASCII 字符的字符串转换为十六进制值。字符串可通过使用 pack() 函数再转换回去。语法bin2hex(string)string必需。要转换的字符串。示例一把 "Shanghai" 转换为十六进制值$str bin2hex("Shanghai");echo($str); // 53686…