前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Java 8 - 04 类型检查、类型推断以及限制

Java 8 - 04 类型检查、类型推断以及限制

作者头像
小小工匠
发布于 2021-08-17 03:34:22
发布于 2021-08-17 03:34:22
88600
代码可运行
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构
运行总次数:0
代码可运行

Pre

当我们第一次提到Lambda表达式时,说它可以为函数式接口生成一个实例。然而,Lambda 表达式本身并不包含它在实现哪个函数式接口的信息。为了全面了解Lambda表达式,women 应该知道Lambda的实际类型是什么 .


类型检查

Lambda的类型是从使用Lambda的上下文推断出来的。 上下文(比如,接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式需要的类型称为目标类型。

举个例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);

类型检查过程可以分解为如下所示。  首先,我们要找出 filter 方法的声明。  第二,要求它是 Predicate (目标类型)对象的第二个正式参数。  第三, Predicate 是一个函数式接口,定义了一个叫作 test 的抽象方法。  第四, test 方法描述了一个函数描述符,它可以接受一个 Apple ,并返回一个 boolean 。  最后, filter 的任何实际参数都必须匹配这个要求

这段代码是有效的,因为我们所传递的Lambda表达式也同样接受 Apple 为参数,并返回一个boolean 。请注意,如果Lambda表达式抛出一个异常,那么抽象方法所声明的 throws 语句也必须与之匹配


同样的 Lambda,不同的函数式接口

有了目标类型的概念,同一个Lambda表达式就可以与不同的函数式接口联系起来,只要它们的抽象方法签名能够兼容.

我们来看下这两个函数式接口

这两个函数式接口 都是 什么也不接受且返回一个泛型 T 的函数, 所以 下面两个赋值是有效的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 Callable<Integer> integerCallable = () -> 18;
 PrivilegedAction<Integer> privilegedAction = () -> 18;

第一个赋值的目标类型是 Callable 第二个赋值的目标类型是PrivilegedAction

再举个栗子 : 同一个Lambda可用于多个不同的函数式接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Comparator<Enginner> enginnerComparator = (e1, e2) -> e1.getJob().compareTo(e2.getJob());
    ToIntBiFunction<Enginner, Enginner> toIntBiFunction = (e1, e2) -> e1.getJob().compareTo(e2.getJob());
    BiFunction<Enginner, Enginner, Integer> toIntFunction = (e1, e2) -> e1.getJob().compareTo(e2.getJob());

Comparator 、 ToIntBiFunction 、 BiFunction 都是返回一个int类型的的函数


菱形运算符

Java 7中已经引入了菱形运算符( <> ),利用泛型推断从上下文推断类型的思想。 一个类实例表达式可以出现在两个或更多不同的上下文中,并会像下面这样推断出适当的类型参数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> listOfStrings = new ArrayList<>();
List<Integer> listOfIntegers = new ArrayList<>();

特殊的void兼容规则

如果一个Lambda的主体是一个语句表达式, 它就和一个返回 void 的函数描述符兼容(当然需要参数列表也兼容)。

举个例子:

以下两行都是合法的,尽管 List 的 add 方法返回了一个boolean ,而不是 Consumer 上下文( T -> void )所要求的 void

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   List<String> stringList = new ArrayList<>();
  // Predicate返回了一个boolean
  Predicate<String> predicate = s -> stringList.add(s);
   // Consumer返回了一个void
   Consumer<String> consumer =    s -> stringList.add(s);

经过了这几个小demo ,是不是能够很好地理解在什么时候以及在哪里可以使用Lambda表达式了。Lambda表达式可以从赋值的上下文、方法调用的上下文(参数和返回值),以及类型转换的上下文中获得目标类型

来个小测验

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
类型检查——为什么下面的代码不能编译呢?
Object o = () -> {System.out.println("Tricky example"); };

答案: 
Lambda表达式的上下文是 Object (目标类型)。但 Object 不是一个函数式接口 。 为了解决这个问题,可以把目标类型改成 Runnable ,它的函数描述符是 () -> void :

Runnable r = () -> {System.out.println("Tricky example"); };

类型推断

刚才已经讨论了如何利用目标类型来检查一个Lambda是否可以用于某个特定的上下文。其实, 它也可以用来做一些略有不同的事:推断Lambda参数的类型,我们来看下。

Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通过目标类型来得到。这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可以在Lambda语法中省去标注参数类型.

举个例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 List<Enginner> goEngineerList = filter(enginnerList,a-> a.getJob().equals("GO"));

参数 a 没有显式类型 .

再举个栗子 ,Lambda表达式有多个参数,代码可读性的好处就更为明显

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     // 没有类型推断,因为给o1,o2指定了Enginner 类型
    Comparator<Enginner> comparator = (Enginner o1, Enginner o2) -> o1.getJob().compareTo(o2.getJob());
    

     //  有类型推断,因为没有给o1,o2指定了Enginner 类型
     Comparator<Enginner> comparator2 = ( o1,  o2) -> o1.getJob().compareTo(o2.getJob());

个人感觉,第二种写法更简单 。

当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略。


使用局部变量

上面所介绍的所有Lambda表达式都只用到了其主体里面的参数。但Lambda表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获Lambda。

举个例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  int num = 1;
  Runnable runnable = ()->System.out.println(num);

这么做虽然有点啰嗦,我们这里想要讨论的是 使用外部的变量有什么限制吗?

如果你想要对这个变量进行操作,之前的lambda就报错了。所以说Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量,但是局部变量必须显式声明为 final.

换句话说,Lambda表达式只能捕获指派给它们的局部变量一次。(注:捕获实例变量可以被看作捕获最终局部变量 this 。) 如上图。

为什么会这样呢?

  • 第一: 实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制
  • 第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式,这种模式会阻碍很容易做到的并行处理.
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/05/16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
terraform简单的开始-简单分析一下内容
紧跟上文:terraform简单的开始-安装和一些配置.先对照上一章文章的main.tf进行一下解读
对你无可奈何
2023/09/15
3400
Grafana 系列-GaC-2-Grafana Terraform Provider 基础
Grafana provider 为 Grafana 提供配置管理资源。是目前 Grafana 官方提供的,覆盖的 Grafana 资源最全的 IaC 工具。
东风微鸣
2023/09/01
3730
Grafana 系列-GaC-2-Grafana Terraform Provider 基础
云计算/云存储---Ceph和Openstack的cinder模块对接方法
在ceph节点两次执行如下语句,两次{your-openstack-server}分别填控制节点和计算节点IP。
yaohong
2019/09/11
2K0
云计算/云存储---Ceph和Openstack的cinder模块对接方法
terraform-远程状态存储
默认情况下,terraform在运行完后,会在当前目录下生成state状态文件,里面存储的是上一次执行成功后的资源状态。
保持热爱奔赴山海
2022/01/11
1.9K0
terraform-远程状态存储
云原生 | Terraform 初体验
Terraform 是一种安全有效地构建、更改和版本控制基础设施的工具(基础架构自动化的编排工具)。
TeamsSix
2022/09/20
1.9K0
云原生 | Terraform 初体验
Terraform实战
Terraform是一种部署技术,任何想要通过基础设施即代码(Infrastructure as Code,IaC)方法来置备和管理基础设施的人,都可以使用这种技术。基础设施指的主要是基于云的基础设施,不过从技术上讲,任何能够通过应用程序编程接口(Application Programming Interface,API)进行控制的东西都可以算作基础设施。基础设施即代码是通过机器可读的定义文件来管理和置备基础设施的过程的
yeedomliu
2024/01/23
4230
Terraform实战
Centos7上部署openstack ocata配置详解
之前写过一篇《openstack mitaka 配置详解》然而最近使用发现阿里不再提供m版本的源,所以最近又开始学习ocata版本,并进行总结,写下如下文档
yaohong
2019/09/11
1.7K0
Centos7上部署openstack ocata配置详解
terraform简单的开始-安装和一些配置
浏览器打开terraform官方主页https://www.terraform.io/ 点击Download Terraform 跳转到程序下载页面:
对你无可奈何
2023/09/13
7820
解决Terraform初始化慢~配置本地离线源
本次实践使用的是Linux/Mac 系统,如果是windows系统有两点不同的配置。
DevOps云学堂
2022/04/25
2K0
解决Terraform初始化慢~配置本地离线源
使用Terraform配置Linode环境
基础架构代码(IaC)是一种软件,使开发人员能够使用高级配置语法构建,管理和配置计算环境。一些好处包括能够实施DevOps最佳实践,流程自动化以及使用版本控制系统在团队中实现更高可见性和协作的机会。
GongAo啊_
2018/09/10
3.7K0
使用Terraform配置Linode环境
Ubuntu 20.04 搭建OpenStack Yoga(allinone)
很多文章都是devstack安装的allinone,我这里使用源码组件手动安装。
全栈程序员站长
2022/11/17
3.7K0
Openstack之Newton
一、组网需求: 1、如下图所示,某用户内网被划分为VLAN 10、VLAN 20、VLAN 30,以实现相互间的2 层隔离; 2、3 个VLAN 对应的IP 子网分别为192.168.10.0/24 、192.168.20.0/24 、192.168.30.0/24,3 个VLAN 通过3 层核心交换机的IP 转发能力实现子网互连。 三、配置要点: 本用例以核心交换机和1 台接入交换机为例说明配置过程。要点如下: 1)在核心交换机配置3 个VLAN,配置下连接入交换机的端口为trunk 口 2)在核心交换机配置3 个SVI 口,分别作为3 个VLAN 对应IP 子网的网关接口,配置对应的IP 地址; 3)分别在3 台接入交换机创建VLAN,为各VLAN 分配Access 口,指定上连核心交换机的trunk 口。本用例以接入交换机Switch A 为例说明配置步骤。
ZHaos
2019/02/27
9820
Terraform 入门
为了使Terraform能够对你的AWS账户进行直接操作,需要将环境变量AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY
yeedomliu
2021/12/10
2.7K0
Terraform 入门
完整部署CentOS7.2+OpenStack+kvm 云平台环境(1)--基础环境搭建
公司在IDC机房有两台很高配置的服务器,计划在上面部署openstack云平台虚拟化环境,用于承载后期开发测试和其他的一些对内业务。 以下对openstack的部署过程及其使用做一详细介绍,仅仅依据本人实际经验而述,如有不当,敬请指出~ ******************************************************************************************************************************** 1 OpenSt
洗尽了浮华
2018/01/22
12.8K2
完整部署CentOS7.2+OpenStack+kvm 云平台环境(1)--基础环境搭建
(十)OpenStack---M版---双节点搭建---Heat安装和配置
↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ 》》》》》》传送门 本章节仅在Controller节点执行 1.Controller节点执行安装和配置 2.验证操作 1.Controller节点执行安装和配置 1.创建数据库 # mysql -uroot -p000000 > create database heat; > grant all privileges on heat.* to 'heat'@'localhost' identified by '000000'; > grant all priv
指剑
2022/07/15
4170
(十)OpenStack---M版---双节点搭建---Heat安装和配置
Terraform快速入门到入土
不过这里值得注意的是,可以看到使用docker ps -a 并没有找到我们停止的容器,所以这里是将这个容器删除了,因为我们正常停止一个容器的时候,使用docker stop,但是在docker ps -a中,我们可以看到已经停止的容器,可以使用docker start 重新将它启动,但是这里是直接将这个容器删除
UzJu@菜菜狗
2022/04/25
2.8K0
Terraform快速入门到入土
(八)OpenStack---M版---双节点搭建---Cinder安装和配置
↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ 》》》》》》传送门 1.创建数据库并授权 2.获得admin凭证执行管理员命令并创建服务证书 3.创建块存储设备API接口 4.Controller节点安装块存储组件并配置 5.初始化块设备服务的数据库 6.配置计算节点以使用块设备存储 7.重启服务并设置开机启动 8.Compute节点安装并配置一个存储节点,此处用Compute节点代替 9.验证操作 1.创建数据库并授权 # mysql -uroot -p000000 > create database c
指剑
2022/07/15
4930
(八)OpenStack---M版---双节点搭建---Cinder安装和配置
从零开始搭建Openstack-Pike(Ubuntu 16.04桌面版)
找一个服务器(自己的电脑也行),使用 Ubuntu-16.04.7 的镜像创建两个虚拟机。
奇门水镜
2021/12/23
2.1K1
从零开始搭建Openstack-Pike(Ubuntu 16.04桌面版)
Terraform 最佳实践:典型文件布局
•global:用于运行各种环境下都要共享的资源(如:Terraform backend - S3、IAM)
东风微鸣
2022/04/22
2.9K0
OpenStack云计算之路-Mitaka 版本
1.1 云计算简介 云计算(英语:cloud computing ),是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息可以按需求提供给计算机各种终端和其他设备。 云计算是继1980年代
惨绿少年
2018/03/30
6.3K0
相关推荐
terraform简单的开始-简单分析一下内容
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验