java三⽬表达式_Java8新特性Lambda表达式
1 Lambda表达式
对于很多计算机语⾔来说,Lambda表达式并不是陌⽣的语法格式,⽽对于Java⽽⾔,它的到来⽐较晚,直到Java8更新之后,Lambda表达式才正式出现在Java语法中。所以如果我们想在Java程序中使⽤Lambda表达式,我们必须将JDK升级到Java8的版本。
Lambda表达式它是基于数学中的λ演算⽽得名,也写作λ表达式,Lambda表达式表⽰⼀个闭包或者匿名函数(⽅法)。在不同的计算机语⾔中,Lambda表达式的符号各有差异。
Lambda表达式在Java中的主要作⽤是为了解决匿名类中匿名⽅法实现的问题(来⾃Oracle的官⽅⽂档的说法)。我们知道在接⼝使⽤上,通常是使⽤⾃定义类(或内部类)的⽅式来实现接⼝。⽽Lambda表达式属于⼀种匿名类的实现⽅式,但前提条件是,该接⼝必须是函数接⼝才能使⽤Lambda表达式。下⾯我们⽤⼀个例⼦来对⽐⼀下,接⼝的普通实现⽅式和Lambda实现⽅式。
上述⽰例中采⽤Lambda表达式实现了Add接⼝,它的代码更加简洁,这也是使⽤Lambda表达式的重要原因。
1.1 函数式接⼝
函数接⼝,我们也称它为SAM接⼝(Single Abstract Method interfaces),中⽂的解释就是"单个抽象⽅法接⼝"。要成为函数接⼝,它的前提条件必须是⼀个接⼝才可以,抽象类是不允许的(即使符合单个抽象⽅法的抽象类也不可以)。函数式接⼝在定义上与普通接⼝的语法规则是⼀样的,但⼀个接⼝要想成为函数式接⼝需要具备以下⼏个条件:
· 接⼝中只能有⼀个抽象⽅法。
· 接⼝中除了⼀个抽象⽅法外,可以含有默认⽅法(default⽅法)和静态⽅法。因为⽅法和静态⽅法都是实现⽅法,并不影响接⼝中抽象⽅法的个数。
· 接⼝中可以拥有Object类中同名的抽象⽅法。
在定义⼀个函数式接⼝时,如果⽆法确认⾃定义的接⼝是否符合函数式接⼝要求,我们可以借助Java为我们提供的注
解"@FunctionalInterface"来进⾏验证,如下⾯⽰例所⽰:
在接⼝定义上添加"@FunctionalInterface"注解,在编译的时候会对接⼝进⾏检查,如果不符合函数式接⼝的定义规则,编译时会报错。
1.2 语法结构
Lambda表达式究竟是什么?从第⼀个Lambda表达式的⽰例上来看,Lambda表达式是⼀个可以向接⼝对象赋值的语句。在Lambda表达
式中,它没有像匿名类那样去实现⼀个接⼝,省略了部分语法格式。同时Lambda表达式还省略了接⼝中的抽象⽅法,以及⽅法中的参数类
型,⽽是在特殊符号后直接编写实现语句(如下图所⽰)。
Lambda表达式组成部分
从赋值的⾓度上来说,我们可以将Lambda表达式看成⼀个对象,它会根据引⽤来推导出Lambda的实现(但必须保证该接⼝是⼀个函数接⼝如上图中的Add接⼝必须是⼀个函数接⼝,只有函数式接⼝才能通过Lambda表达式进⾏推导)。
⼀个Lambda表达式由参数部分和实现部分组成,中间⽤特殊符号连接符号"->"进⾏链接。在使⽤的时候,可根据具体环境继续简写。
1.参数部分:使⽤括号"()"代表参数列表。如果只有⼀个参数,可以省略括号。多个参数应使⽤逗号进⾏分割,要特别注意没有参数的表达式不能省略括号。括号中的参数类型可以添加、可以省略,省略时它会⾃⼰进⾏推导,但必须保证与接⼝⽅法的参数顺序⼀致。
2.⽤⼤括号"{}" 代表⽅法体(如果⽅法内只有⼀条语句,可以省去),括号内书写⽅法内容。如果存在返回值,且⽅法内只有⼀条语句时,可以省略return关键字,但该语句表达式必须能够表达出返回值的含义。
Lambda表达式简略写法
1.3 ⽬标类型推导
在使⽤Lambda表达式的时候,Lambda表达式以赋值的形式传递给接⼝引⽤,这时就需要Lambda表达式进⾏⾃动推导。例如在"Add
add=(a,b)->a+b;"这句代码中,它⾸先要推导出Lambda表达式的主体是函数式接⼝"Add"的实现,其次
匹配参数,参数"a"和"b"必须与接⼝中的唯⼀抽象⽅法数量匹配,并⾃动匹配参数类型。最后匹配表达式是否与抽象⽅法的返回类型相符。这些推导在编译的时候会检查。
Lambda表达式在编写的时候要注意以下四点:
1.引⽤接⼝必须是函数式接⼝。
2.表达式和接⼝抽象⽅法的参数数量、类型和位置上必须⼀⼀对应。
3.Lambda表达式中返回值与接⼝抽象⽅法的返回类型相同(类型可兼容,可返回类型的⼦类)。
4.Lambda表达式内抛出的异常和接⼝抽象⽅法throws的类型相同或兼容。
Lambda表达式在很多场景中都可以使⽤,例如在下述环境中,Lambda表达式可以根据上下⽂环境进⾏推导。
1.返回语句推导:同样在⽅法内的返回语句中,可以从⽅法的返回类型上进⾏推导。下⾯⽰例中,Person类中需要返回⼀个Comparator 外⽐较器对象,我们可以直接在return语句中使⽤Lambda表达式来实现(两个表达式效果⼀样)。
java生成随机数的方法
Lambda返回语句推导
2.数组初始化器推导:在创建数组初始化时,也可以使⽤Lambda表达式的进⾏类型的推导(必须是符
合函数接⼝的类型组成的数组,如Runnable(线程),FileFilter(标准IO中的⽂件过滤器等,这些接⼝在Java8中,都被注解了函数式接⼝)。
在下⾯⽰例中,我们定义的NumberRandom接⼝,包含了⼀个⽤于⽣成随机数的抽象⽅法。当我们创建NumberRandom接⼝数组时,我们可以使⽤Lambda表达式进⾏数组的创建。
Lambda表达式应⽤于数组初始化
在上述⽰例中,当数据元素初始化的时候,我们没有声明个数,⽽是采⽤了直接赋值的⽅式进⾏数组初始化。给数组赋值的每⼀个元素都由Lambda表达式构成,由此可见Lambda可以推导出正确的数组元素类型。
3.⽅法和构造⽅法参数推导:当⼀个函数式接⼝作为参数时,我们也可以直接使⽤Lambda表达式,编译的时候会推导出符合参数的类型。在下⾯⽰例,⾃定义类FindFile中设置了⽂件过滤器,来完成⽂件查时的过滤功能,我们可以通过Lambda表达式来传递参数。
Lambda表达式作为参数使⽤
在设置⽂件过滤器时(⾏11), setFileFilter⽅法的参数是FileFilter,这时表达式被推导为FileFilter。
4.Lambda函数体推导:⼤多数情况下,Lambda表达式的类型是⽐较明确的,例如在没有重载⽅法的情况下,⽅法的参数的类型是明确的,所以Lambda表达式能够清楚的知道⾃⼰应该推导出什么类型,我们也称这样为"显式类型"。
但如果⽅法是重载⽅法,这个时候Lambda表达式被推导成什么类型完全是由参数决定的。这时表达式作为参就⽆法⾃⾏推导,⽽是去依赖
表达式中的参数数量进⾏推导,我们称这样的⽅式为"隐式类型"。在下⾯⽰例中,我们可以⽤参数的个数或类型来推导出函数的类型。
Lambda函数推导
在上述⽰例中,⽅法setFunction有两个重载⽅法,分别需要提供FileFilter和Runnable接⼝对象。恰好这两个接⼝对都是函数式接⼝,表
达式的推断就要根据表达式中的参数来确定。
⽽在极端情况下,参数的个数都⼀样,就会存在分歧(⼆义性),这个时候我们就需要将Lambda进⾏转型。例如下⾯⽰例中,参数的数量也
⽆法推导出Lambda表达式的类型,我们只能通过强制转化告诉虚拟机该如何推导。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。