首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java 泛型解惑之<? extends T>和<? super T>

Java 泛型解惑之<? extends T>和<? super T>

作者头像
JavaEdge
发布于 2018-10-11 06:57:12
发布于 2018-10-11 06:57:12
3K00
代码可运行
举报
文章被收录于专栏:JavaEdgeJavaEdge
运行总次数:0
代码可运行

1 为什么要用通配符和边界?

使用泛型的过程中,经常出现一种很别扭的情况 比如我们有Fruit类,和它的派生类Apple

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Fruit {}
class Apple extends Fruit {}

然后有一个最简单的容器Plate类 盘子里可以放一个泛型的“东西” 我们可以对这个东西做最简单的“”和“”的动作:set( )get( )方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Plate<T>{
    private T item;
    public Plate(T t){item=t;}
    public void set(T t){item=t;}
    public T get(){return item;}
}

现定义一个“水果盘”,逻辑上水果盘当然可以装苹果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Plate<Fruit> p = new Plate<Apple>(new Apple());

但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
error: incompatible types: Plate<Apple> cannot be converted to Plate<Fruit>

实际上,编译器认定的逻辑是这样的:

  • 苹果 IS-A 水果
  • 装苹果的盘子 NOT-IS-A 装水果的盘子

所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系 所以我们不可以把Plate<Apple>的引用传递给Plate<Fruit>

为了让泛型用起来更舒服,Sun的大师们就想出了<? extends T>和<? super T>的办法,来让”水果盘子“和”苹果盘子“之间发生正当关系

2 上界

下面就是上界通配符(Upper Bounds Wildcards)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Plate<extends Fruit>

一个能放水果以及一切是水果派生类的盘子 再直白点就是:啥水果都能放的盘子 这和我们人类的逻辑就比较接近了 Plate<? extends Fruit>和Plate<Apple>最大的区别就是:Plate<? extends Fruit>是Plate<Fruit>及Plate<Apple>的基类 直接的好处就是,我们可以用“苹果盘”给“水果盘”赋值了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Plate<? extends Fruit> p = new Plate<Apple>(new Apple());

再扩展一下,食物分成水果和肉类,水果有苹果和香蕉,肉类有猪肉和牛肉,苹果还有两种青苹果和红苹果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Lev 1
class Food{}

//Lev 2
class Fruit extends Food{}
class Meat extends Food{}

//Lev 3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork extends Meat{}
class Beef extends Meat{}

//Lev 4
class RedApple extends Apple{}
class GreenApple extends Apple{}

在这个体系中,上界通配符Plate<? extends Fruit>覆盖下图中蓝色的区域

3 下界

相对应的下界通配符(Lower Bounds Wildcards)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Plate<super Fruit>

表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子 Plate<? super Fruit>是Plate<Fruit>的基类,但不是Plate<Apple>的基类 对应刚才那个例子,Plate<? super Fruit>覆盖下图中红色的区域。

4 上下界通配符的副作用

边界让Java不同泛型之间的转换更容易了。但不要忘记,这样的转换也有一定的副作用。那就是容器的部分功能可能失效。

还是以刚才的Plate为例。我们可以对盘子做两件事,往盘子里set( )新东西,以及从盘子里get( )东西

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Plate<T>{
    private T item;
    public Plate(T t){item=t;}
    public void set(T t){item=t;}
    public T get(){return item;}
}

4.1 上界<? extends T>不能往里存,只能往外取

<? extends Fruit>会使往盘子里放东西的set( )方法失效 但取东西get( )方法还有效

比如下面例子里两个set()方法,插入Apple和Fruit都报错

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Plate<? extends Fruit> p = new Plate<Apple>(new Apple());

//不能存入任何元素
p.set(new Fruit());    //Error
p.set(new Apple());    //Error

//读取出来的东西只能存放在Fruit或它的基类里。
Fruit newFruit1=p.get();
Object newFruit2=p.get();
Apple newFruit3=p.get();    //Error

编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道 可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?编译器在看到后面用Plate<Apple>赋值以后,盘子里没有被标上有“苹果”。而是标上一个占位符:capture#1,来表示捕获一个Fruit或Fruit的子类,具体是什么类不知道,代号capture#1 然后无论是想往里插入Apple或者Meat或者Fruit编译器都不知道能不能和这个capture#1匹配,所以就都不允许

所以通配符<?>和类型参数<T>的区别就在于,对编译器来说所有的T都代表同一种类型 比如下面这个泛型方法里,三个T都指代同一个类型,要么都是String,要么都是Integer...

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public <T> List<T> fill(T... t);

但通配符<?>没有这种约束,Plate<?>单纯的就表示:盘子里放了一个东西,是什么我不知道

4.2 下界<? super T>不影响往里存,但往外取只能放在Object对象里

使用下界<? super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());

//存入元素正常
p.set(new Fruit());
p.set(new Apple());

//读取出来的东西只能存放在Object类里。
Apple newFruit3=p.get();    //Error
Fruit newFruit1=p.get();    //Error
Object newFruit2=p.get();

因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制 既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。

5 PECS原则

最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了

  1. 频繁往外读取内容的,适合用上界Extends
  2. 经常往里插入的,适合用下界Super
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.10.09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
contentWindow.onload "Blocked a frame with origin "http://localhost:5000" from accessing a cross-origin frame."
contentWindow.onload "Blocked a frame with origin "http://localhost:5000" from accessing a cross-origin frame."
回复回复点赞举报
推荐阅读
窗口间通信方案——postMessage
postMessage 是 html5 引入的 API,postMessage 方法允许来自不同源的脚本采用异步方式进行通信,其实同源不同页面的脚本也可以采用 postMessage 方法进行通信。
用户6256742
2024/05/31
2730
postMessage与postMessage跨域
HTML5学堂今日postMessage跨域教学流程 先为大家讲解postMessage的基本知识 之后,我们书写一个实例:使用静态的iframe,实现A域前端页面与B域前端页面之间的数据传递 最后,我们使用JS动态的生成iframe,实现A域的前端页面与B域的前端页面“互通”,并在B域中使用AJAX申请B域的后台数据 1 postMessage通信的方法与事件 postMessage的跨域方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。 想要完成“一个域”与“另
HTML5学堂
2018/03/13
3.2K0
postMessage与postMessage跨域
Web中的窗口通讯方式及使用(postMessage/MessageChannel/BroadcastChannel)
三种常用的跨窗口通信技术:postMessage、MessageChannel 和 BroadcastChannel。
码客说
2024/01/06
2.4K0
postMessage的使用
postMessage是H5的API,用来解决跨页面通信的。postMessage的使用分为发送方和接收方。
全栈程序员站长
2022/09/14
1.1K0
iframe 与 postMessage 方法
既是发消息,就有发送方与接收方,发送方要调用 postMessage 方法,接收方要注册 onmessage 事件处理函数,处理接收的消息。
挥刀北上
2023/06/20
8880
iframe 与 postMessage 方法
【JS】504- HTML5 之跨域通讯(postMessage)
本文来自公众号【前端早读课】,最近工作中用在使用 postMessage ,所以一起分享给大家一下~
pingan8787
2020/02/26
1.9K0
【JS】504- HTML5 之跨域通讯(postMessage)
跨域方法汇总
做 Web 开发经常需要面对跨域问题,跨域问题的根源是浏览器安全中的同源策略,比如说,对于 http://www.a.com/1.html 来说:
四火
2022/07/18
6540
跨域方法汇总
《前端实战总结》之使用postMessage实现可插拔的跨域聊天机器人
首先要强调的是跨域的安全限制都是对浏览器端来说的,服务器端是不存在跨域安全限制的。我们常用的跨域技术主要有如下几种:
徐小夕
2019/11/14
1.2K0
《前端实战总结》之使用postMessage实现可插拔的跨域聊天机器人
postMessage使用
参考自文档:https://www.runoob.com/js/met-win-postmessage.html
蓓蕾心晴
2022/06/30
5910
web messaging与Woker分类:漫谈postMessage跨线程跨页面通信
iframe_contentWindow.postMessage(message, targetOrigin, [transfer]);
周陆军
2021/07/03
2.4K0
postMessage 还能这样玩
在日常工作中,消息通信是一个很常见的场景。比如大家熟悉 B/S 结构,在该结构下,浏览器与服务器之间是基于 HTTP 协议进行消息通信:
ConardLi
2020/11/26
2.1K0
postMessage 还能这样玩
详解使用postMessage解决iframe跨域通信问题
这周碰到一个让人头疼的需求:要在我的web项目中嵌入另一个第三方web项目。第一时间想到的就是用iframe了,但问题来了,我和第三方web项目是有交互的,这就违反同源策略了,处理跨域问题是最让人头疼的事之一。
用户1289394
2021/10/13
4.9K0
JSONP && CORS
  前天面试被问到了跨域的问题,自我感觉回答的并不理想,下面我就分享一下整理后的总结分享给大家
超然
2018/08/03
1.4K0
JSONP && CORS
通过postMessage进行跨域通信
最近工作中遇到一个需求,场景是:h5页作为预览模块内嵌在pc页中,用户在pc页中能够做一些操作,然后h5做出响应式变化,达到预览的效果。
Keller
2021/12/14
8040
前端:跨域
同源策略(same-origin policy)是一个重要的安全策略。它用于限制从一个源(origin)加载的文档或脚本,如何与另一个源(origin)的资源进行交互。
WEBJ2EE
2020/05/22
1.3K0
前端:跨域
如何利用postMessage窃取编辑用户的Cookie信息
某天,当我在做某个项目的漏洞测试时,在登录的一些HTTP请求记录中,我发现了一种利用postMessage方式窃取和编辑用户Cookie的方法。由于该测试是邀请测试,出于保密,我只能在下文中和大家分享一些方法思路。
FB客服
2019/05/09
1.7K0
如何利用postMessage窃取编辑用户的Cookie信息
深入理解 <iframe>的双向通信:从基础到实战
在现代 Web 开发中,<iframe> 是一个常用的 HTML 元素,用于在页面中嵌入另一个网页。然而,<iframe> 的嵌入页面与父页面之间的通信一直是一个复杂且容易出错的问题。本文将深入探讨 <iframe> 的双向通信机制,从基础概念到实战应用,帮助开发者彻底掌握这一技术。
Front_Yue
2025/03/12
5900
深入理解 <iframe>的双向通信:从基础到实战
你真的了解跨域吗
相信每个前端对于跨域这两个字都不会陌生,在实际项目中应用也很多,但跨域方法的多种多样让人目不暇接,前段时间公司同事出现了跨域问题,又一时找不到问题所在,所以在此总结下跨域知识,一篇由浅入深的万字Web基操文
isboyjc
2022/03/28
2.6K0
你真的了解跨域吗
postMessage实现跨域通信
web通信(洋名:web messaging)是一种文档中独立的浏览上下文间的DOM不会被恶意的跨域脚本暴露数据分享方式。
javascript.shop
2019/09/04
1.8K0
postMessage实现跨域通信
web跨域解决方案
阅读目录 什么是跨域 常用的几种跨域处理方法: 跨域的原理解析及实现方法 总结 摘要:跨域问题,无论是面试还是平时的工作中,都会遇到,本文总结处理跨域问题的几种方法以及其原理,也让自己搞懂这方面的知识,走起。 什么是跨域     在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。   JavaScript这个安全策
hbbliyong
2018/03/06
2.9K0
web跨域解决方案
相关推荐
窗口间通信方案——postMessage
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档