Java泛型中的子类型化

泛型类型为Java程序引入了新的类型安全范围。 在同一类型上,泛型类型可以表现得很好,尤其是在使用通配符时 。 在本文中,我想解释子类型如何与Java泛型一起工作。

关于泛型类型子类型化的一般思考

不同泛型类型相同的类或接口的不定义亚型层级线性尽可能通用参数类型的子类型的层次结构。 例如,这意味着List <Number>不是List <Integer>的超类型。 下面的突出示例很好地说明了为什么禁止这种子类型化:

// assuming that such subtyping was possible
ArrayList<Number> list = new ArrayList<Integer>();
// the next line would cause a ClassCastException
// because Double is no subtype of Integer
list.add(new Double(.1d))

在进一步详细讨论之前,让我们首先考虑一下有关类型的一般信息:类型为程序引入了冗余。 当您将变量定义为Number类型时,请确保该变量仅引用知道如何处理Number定义的任何方法(例如Number.doubleValue)的对象。 这样,您可以确保可以安全地在变量当前表示的任何对象上调用doubleValue,并且不再需要跟踪变量引用的对象的实际类型。 (只要引用不为null。null引用实际上是Java严格类型安全性的少数例外之一。当然,null的“对象”不知道如何处理任何方法调用。)但是,如果您试图将String类型的对象分配给此Number类型的变量,Java编译器将认识到该对象实际上不理解Number所需的方法,并且会引发错误,因为它不能保证将来可能会调用例如doubleValue将被理解。 但是,如果我们缺少Java中的类型,则程序不会仅凭此更改其功能。 只要我们从不进行错误的方法调用,那么没有类型的Java程序就等效。 从这个角度来看,类型仅仅是为了防止我们的开发人员在愚蠢的事情上夺走一点自由。 此外,类型是隐式记录程序的一种好方法。 (诸如Smalltalk之类的其他编程语言不知道类型,并且除了在大多数时候困扰之外,这也有其好处。)

有了这个,让我们回到泛型。 通过定义通用类型,您可以允许通用类或接口的用户为其代码添加某种类型安全性,因为他们可以限制自己仅以某种方式使用您的类或接口。 例如,当您通过定义List <Number>将List定义为仅包含Numbers时,建议您每次尝试将String类型的对象添加到此列表中时,Java编译器都将引发错误。 在使用Java泛型之前,您只需要相信列表仅包含数字即可。 当您将集合的引用交给第三方代码中定义的方法或从该代码接收到集合时,这可能会特别痛苦。 使用泛型,即使在编译时,您也可以确保List中的所有元素都是某个超类型。

同时,通过使用泛型,您会泛型类或接口失去一些类型安全性。 例如,当您实现通用列表时

class MyList<T> extends ArrayList<T> { }

您不知道MyList中T的类型,并且必须期望该类型可以像Object一样简单。 这就是为什么您可以限制通用类型要求某些最小类型的原因:

class MyList<T extends Number> extends ArrayList<T> {double sum() { double sum = .0d;for(Number val : this) {sum += val.doubleValue();}return sum;}
}

这使您可以假定MyList中的任何对象都是Number的子类型。 这样,您就可以在泛型类中获得某种类型的安全性。

通配符

Java中的通配符等效于说出任何类型 。 因此,在实例化类型(即定义泛型类的某些实例应代表哪种具体类型)时,不允许使用通配符。 例如,在将对象实例化为新的ArrayList <Number>时发生类型实例化,其中您隐式调用包含在其类定义中的ArrayList的类型构造函数

class ArrayList<T> implements List<T> { ... }

ArrayList <T>是带有单个参数的简单类型构造函数。 因此,在ArrayList的类型构造函数定义(ArrayList <T>)中或在此构造函数的调用(新ArrayList <Number>)中,都不允许使用通配符。 但是,如果仅引用类型而不实例化新对象,则可以使用通配符,例如在局部变量中。 因此,允许以下定义:

ArrayList<?> list;

通过定义此变量,可以为任何通用类型的ArrayList创建占位符。 但是,由于对通用类型的这种限制很小,因此无法通过此变量对其的引用将对象添加到列表中。 这是因为您对变量列表所代表的泛型做出了这样的一般假设,即添加一个类型为String的对象并不安全,因为超出列表的列表可能需要某种其他任何子类型的对象。 通常,此必需的类型是未知的,并且不存在任何类型的子类型的对象,可以安全地添加该对象。 (例外是取消了类型检查的空引用。但是,您永远不应在集合中添加空值。)同时,从列表中删除的所有对象都将是对象类型,因为这是关于a的唯一安全假设此变量表示的所有可能列表的常见超类型 。 因此,您可以使用extends和super关键字形成更复杂的通配符:

ArrayList<?> list = new ArrayList<List<?>>();

在此示例中,由于不将通配符应用于类型实参,而不应用于构造的类型本身,因此满足了不得使用通配符类型构造ArrayList的要求。

至于泛型类的子类型化,我们可以总结一下,如果原始类型是子类型,并且泛型类型都是彼此的子类型,则某些泛型类型是另一种类型的子类型。 因此,我们可以定义

List<? extends Number> list = new ArrayList<Integer>();

因为原始类型ArrayList是List的子类型,并且因为泛型Integer是?的子类型? 扩展Number。

最后,请注意,通配符List <?>是List <?的快捷方式。 扩展Object>,因为这是一种常用的类型定义。 但是,如果泛型类型构造函数确实实施了另一个较低的类型边界,例如

class GenericClass<T extends Number> { }

变量GenericClass <?>而是GenericClass <?的快捷方式。 扩展Number>。

取放原则

这种观察将我们引到了“ 获取-放出”原理 。 另一个著名的例子可以最好地解释这一原理:

class CopyClass {<T> void copy(List<T> from, List<T> to) {for(T item : from) to.add(item);}
}

此方法定义不是很灵活。 如果您有一些列表List <Integer>,则无法将其内容复制到某些List <Number>甚至List <Object>。 因此,“获取和放置”原则规定,当您仅从通用实例(通过return参数)读取对象时,应始终使用下限通配符(?extends),而在以下情况下应始终使用上限通配符(?super)。您只提供通用实例方法的参数。 因此,更好的MyAddRemoveList实现如下所示:

class CopyClass {<T> void copy(List<? extends T> from, List<? super T> to) {for(T item : from) to.add(item);}
}

由于您仅从一个列表中读取内容,然后再写入另一个列表中,因此很遗憾,这是很容易被忽略的,您甚至可以在Java核心API中找到不采用“获取与放置”原理的类。 (请注意,上述方法还描述了泛型类型构造函数。)

请注意,类型List <? 扩展T>和List <? 超级T>都没有List <T>的要求那么具体。 还要注意,这种子类型对于非通用类型已经是隐式的。 如果定义的方法要求使用Number类型的方法参数,则可以自动接收任何子类型的实例,例如Integer。 但是,即使期望超型Number,也始终可以安全地读取您收到的此Integer对象。 而且由于无法回该引用,即您不能用Double的实例覆盖Integer对象,因此Java语言不需要通过声明方法签名(如void someMethod(<?扩展Number> number)。 同样,当您答应从方法中返回整数时,调用者只需要一个Number类型的对象,您仍然可以从方法中返回( )任何子类型。 同样,由于无法从假设的返回变量中读取值,因此在方法签名中声明返回类型时,不必通过通配符放弃这些假设的读取权限。

参考: 我的Java日常博客中来自我们JCG合作伙伴 Rafael Winterhalter的Java泛型子类型化 。

翻译自: https://www.javacodegeeks.com/2013/12/subtyping-in-java-generics.html

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

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

相关文章

css3之盒模型

什么是盒模型&#xff1f; css中的每个元素都是一个盒模型&#xff0c; 包括html body元素&#xff0c; 浏览器解析css的时候也会把每个元素看成一个盒子来解析。 盒模型具备的属性有&#xff1a; content 、padding 、margin、border 、background等 盒模型的分类&#xff1…

小朋友排队|2014年蓝桥杯B组题解析第十题-fishers

小朋友排队 n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列&#xff0c;但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。开始的时候&#xff0c;所有小朋友的不高兴程度都是0。 如果某个小朋友第一次被要求交换&#xff0c;则他的不高兴…

学计算机的误解,让人误解的六大专业

原标题&#xff1a;让人误解的六大专业隔行如隔山&#xff0c;很多人喜欢看名字猜专业&#xff0c;所以导致很多大学专业被人误解。其实了解一个专业不能仅仅凭借它的名字&#xff0c;也不能断章取义&#xff0c;只取片面意思。接下来就让我们来了解一下有哪些被人误解的专业吧…

杂项:轮询

ylbtech-杂项&#xff1a;轮询1.返回顶部 1、轮询&#xff08;Polling&#xff09;是一种CPU决策如何提供周边设备服务的方式&#xff0c;又称“程控输出入”&#xff08;Programmed I/O&#xff09;。轮询法的概念是&#xff0c;由CPU定时发出询问&#xff0c;依序询问每一个周…

python类属性的调用方法_问一个关于PYTHON类属性调用方法的问题

def baidu(self): self.APP_ID2180368 self.API_KEYEYAvb2oTaGa9oSNs5S2yx6v self.SECRET_KEYdmgvBELGq9cMvk2uSPqLUaLUpEng02D self.aipAipOcr(self.APP_ID,self.API_KEY,self.SECRET_KEY) #1.这里定义的self.aip 3. 如果 我把这个self.aip定义在__init__(self)里面就不会报错…

js和css实现手机横竖屏预览思路整理

实现效果&#xff0c;如上图。 首先&#xff0c;实现手机页面在PC端预览&#xff0c; 则先在网上找到一个手机的背景图片&#xff0c;算好大概内间距&#xff0c;用来放预览的页面&#xff0c;我这里是给手机预览页面的尺寸按iphone5的尺寸来的&#xff1b; 一个手机页面在这里…

Lambda,会序列化吗?

因此&#xff0c;我一直在思考Tyrus项目所需的增强功能&#xff0c;该功能允许用户广播到跨机器集群连接到URL的客户端子集。 有多种方法可以做到这一点&#xff1b; 但是自从我使用JDK 8以来&#xff0c;这个问题肯定看起来像钉子。 为此&#xff0c;我创建了一个简单的单元测…

thinkphp日志泄漏漏洞_【Windows高危漏洞预警】CVE20200601,影响关键加密功能

一、事件报告2020年伊始&#xff0c;NSA发现了一个影响Microsoft Windows加密功能的严重漏洞(CVE-2020-0601)。证书验证漏洞允许攻击者破坏Windows验证加密信任的方式&#xff0c;并且可以启用远程代码执行。该漏洞会影响Windows 10和Windows Server 2016/2019以及依赖Windows的…

第一章计算机网络概述答案,第一章 计算机网络概述[3]

1-07 试在下列条件下比较电路交换和分组交换。要传送的报文共x(bit)。从源站到目的站共经过k段链路&#xff0c;每段链路的传播时延为d(s)&#xff0c;数据率为b(b/s)。在电路交换时电路的建立时间为S(s)。在分组交换时分组长度为p(bit)&#xff0c;且各结点的排队等待时间可忽…

meta标签的用处详解

meta标签的用处&#xff1a; 用来描述html文档的一个属性。列如作者。日期和时间&#xff0c;网页描述&#xff0c;关键字&#xff0c;页面刷新等。 是文档最基本的元数据 元数据&#xff08;metadata&#xff09;&#xff1a; 用来概括描述数据的一些基本数据 meta 标签的使用…

最小路径算法(Dijkstra算法和Floyd算法)

1.单源点的最短路径问题&#xff1a;给定带权有向图G和源点v&#xff0c;求从v到G中其余各顶点的最短路径。 我们用一个例子来具体说明迪杰斯特拉算法的流程。 定义源点为 0&#xff0c;dist[i]为源点 0 到顶点 i 的最短路径。其过程描述如下&#xff1a; 步骤dist[1]dist[2]di…

react如何监听路由url变化

"componentWillReceiveProps" "shouldComponentUpdate" "componentWillUpdate" "render" "componentDidUpdate" 使用这些生命周期钩子可以监听到路由相同&#xff0c;参数不同的变化&#xff0c;但是监听不到完全不相同的ur…

python 购物车程序_python_购物车程序

#需求1.启动程序后&#xff0c;让用户输入工资&#xff0c;然后打印商品列表 2.允许用户根据商品编号购买商品 3.用户选择商品后&#xff0c;检测余额是否够&#xff0c;够就直接扣款&#xff0c;不够就提醒 4.可随时退出&#xff0c;退出时&#xff0c;打印已购买商品和余额 #…

matlab tsai手眼标定程序代码_标定系列一 | 机器人手眼标定的基础理论分析

旷视MegMaster机器人系列是旷视自主研发的一系列AI智能机器人硬件设备&#xff0c;基于旷视全球领先的人工智能算法及机器人技术&#xff0c;可实现搬运、分拣、托举、存储等功能&#xff0c;被广泛应用于物流仓储、工厂制造等场景。旷视SLAM组主要负责多传感器建图、定位、标定…

Java8中的外观(JavaFX8)

JavaFX8在外观方面进行了一些更改 &#xff0c;其中最相关的是新CSS API &#xff0c;它允许您为控件以及已公开的Skin类创建新CSS属性和伪类。 使用CSS可以改变控件的很多外观&#xff0c;但是CSS只能实现很多功能&#xff0c;而这正是Skin类的用处。从“ UI控件体系结构”快…

600分理科选计算机专业,天津600分左右,计算机或电子信息专业,怎么选院校?...

原标题&#xff1a;天津600分左右&#xff0c;计算机或电子信息专业&#xff0c;怎么选院校&#xff1f;想学计算机或电子信息&#xff0c;一定是偏理选科对吧&#xff01;所谓的换算&#xff0c;是指的高考后出了全天津市不分文理的总排名后&#xff0c;如何换算成相当于天津市…

[TypeScript] Export public types from your library

If youre a library author, its useful to expose your public types as interfaces, to allow your consumers to extend them if needed. For example: To resolve the issues, we can do : // typings.d.tsinterface JQuery {hideChildren(): JQuery } 转载于:https://www.…

详解Asp.Net Core中的Cookies

目录 详解Asp.Net Core中的cookies搞懂cookiesAsp.Net中cookies的实现从http中获取cookies将cookies写入http中总结及感想详解Asp.Net Core中的cookies 搞懂cookies 我之前写过一篇文章来介绍cookies&#xff0c;如果你对cookies不是很了解请移步理解cookies这篇文章&#xff0…

为什么坚持使用JavaScript

由于JavaScript似乎征服了世界&#xff0c;因此与之相关的所有技术&#xff08;例如Node.js&#xff09;也开始兴起。 所有中间语言&#xff0c;包括TypeScript &#xff0c; CoffeeScript &#xff0c; ClojureScript和Google的Dart都已设定为目标&#xff0c;以使其更容易用J…

python tkinter选择路径控件_Python3 Tkinter选择路径功能的实现方法

效果基于Python3。 在自己写小工具的时候因为这个功能纠结了一会儿&#xff0c;这里写个小例子&#xff0c;供有需要的参考。 小例子&#xff0c;就是点击按钮打开路径选择窗口&#xff0c;选择后把值传给Entry输出。 效果预览 这是选择前&#xff1a;选择&#xff1a;选择后&a…