Lambda 表达式
初识 Lambda
-
什么是 Lambda 表达式?
- 自 Java 8 增加的新特性
- 一个匿名方法(一个没有名字的方法,在定义变量的时候定义的方法)
-
为什么使用 Lambda ?
-
简洁实现接口对接;
-
虽然可以使用 Lambda 表达式对某些接口进行实现,但是并不是所有的接口都可以使用 Lambda 来实现;
-
实现要求:要实现的抽象方法只能是一个;
-
在 Java 8 中对接口实现了一个新特性
default
,被此方法修饰的方法在类中有一个默认实现,所以不影响使用 Lambda 的实现;
-
-
@FunctionalInterface:
- 一个用来修饰函数式接口的,接口中的抽象方法只能有一个;
// 错误代码展示
@FunctionalInterface
interface Comparer{
int compare(int a,int b);// 方法一
int show(int a,int b); // 方法二
}
<font color=red size=2>↑ ↑ ↑ 错误代码展示 ↑ ↑ ↑</font>
-
代码示例
public class DEMO{
public static void main(String[] args){
// 方法一:使用接口实现类
Comparator comparator = new MyComparator();
//方法二:使用匿名内部类
Comparator comparator = new Comparator(){
@Override
public int compare(int a,int b){
return a-b;
}
}
//方法三:使用Lambda表达式
Comparator comparator2 = (a,b) -> a-b;
}
}
// 一个接口
interface Comparator{
int compare(int a,int b);
}
//方法一:使用实现类实现接口
class MyComparator implements Comparator{
@Override
public int compare(int a,int b){
return a-b;
}
}
Lambda 基础语法
-
基础语法:
- Lambda 是一个匿名函数
- 包含:参数列表、方法体、(不包含:返回值类型、方法名)
-
元素:
()
:用来描述参数列表{}
:用来描述方法体->
:分隔 Lambda 运算法,读作:goes to
-
几种情况:
1、无参 无返回
// 无参 无返回 LambdaTest lam1 = ()->{ Sout("Hello World!"); } lam1.test(); // 结果:输出`Hello World!`
2、单个参 无返回
// 无参 单个返回值 LambdaTest lam2 = (int a)->{ Sout(a); } lam2.test(10); // 结果:输出`10`
3、多个参 无返回
LambdaTest lam3 = (int a,int b)->{ Sout(a+b); } lam3.test(10,11); // 结果:输出`21`
4、无参 有返回
LambdaTest lam4 = ()->{ Sout(100); return 100; } int ret = lam4.test(); // 结果:输出`100` ; 返回 `100`
5、有参数 有返回
LambdaTest lam5 = (int a)->{ Sout(a*2); return a*2; } int ret = lam5.test(10); // 结果:输出`20` ; 返回 `20`
↑ ↑ ↑ 要注意 一定要定义相应的 函数式接口
↑ ↑ ↑
Lambda 语法精简
- 错误代码演示:
interface Lamx{
int test(int a,int b);
}
Lamx lamx = (int a,int b)->{
Sout("a=" + a + "\nb=" + b);
// return a+b;
}
上面报错了?Lambda表达式如何知道有返回值?
Lambda 因为定义的接口有了说明,比如接口中写
int test(int a);
第一个int
表示返回值类型,第二个int
表示参数类型,所以 舍去了return
就会报错;
既然在定义接口的时候已经定义了类型,那么是不是可以在写入参数的时候,将参数的类型描述给省略掉呢?
-
精简1:省略参数描述
int
代码演示:
interface Lamx{
int test(int a,int b);
}
Lamx lamx = (a,b)->{
Sout("a=" + a + "\nb=" + b);
return a+b;
}
接口中已经说明了有int
类型的返回值,所以不写return
是错误的;
两个必须同时省略,不能一个写一个不写;
-
精简2:省略小括号
()
代码演示:
interface Lamx{
int test(int a);
}
Lamx lamx = a -> {
Sout("a=" + a );
return a;
}
当 入参 只有一个的时候,可以省略入参的 括号;
- 精简3:省略大括号
{}
interface Lamx{
int test(int a);
}
Lamx lamx = a -> Sout( a );
当 函数 只有一行的时候,可以省略大括号;
- 精简4:省略
return
interface Lamx{
int test(int a);
}
Lamx lamx = a -> a;
如果唯一一条语句是return
语句,那么省略大括号的同时,也必须省略return
;
Lamx lamx = (a,b)->a+b;
lamx.test(5,10);
Lambda 语法进阶
- Lambda 调用静态方法
- 代码示例:
public class Test{
public static void main(String[] args){
// 实现方式一: 传统 Lambda 语法
Lamx lamx = a -> change(a);
// 实现方式二: 双" : "语法
Lamx lamx = Test::change;
}
public static int change(int a){
return a*2;
}
}
将 Lambda 指向一个已经实现的方法;语法规则 -- 方法隶属者::方法名
此处的change
是一个静态方法,隶属者就是Test
类,所以就是Test::change
- 注意事项:
- 参数的数量和类型一定要和方法中定义的一致
- 返回型一定要和接口中定义的方法一致
- Lambda 调用构造方法
- 保留
// 构造方法
public class Person{
public String name;
public int age;
public Person(){
Sout("Person类的 无参 构造方法执行了;");
}
public Person(String name,int age){
this.name = name;
this.age = age;
Sout("Person类的 有参 构造方法执行了;");
}
}
// Lambda 语法
public class Lamx{
public static void main(String[] args){
// 原始方法
PersonCreater creater1 = () -> {
return new Person();
};
// 简化方法
PersonCreater creater2 = () -> new Person();
// 构造方法的引用
PersonCreater creater3 = Person::new;
Person a = creater3.getPersonNull();
Person a = creater3.getPersonDouble("张三",18);
}
}
//接口中要说明 返回值类型 方法参数!!!
interface PersonCreater{
Person gerPersonNull();
Person getPersonDouble(String name,int age);
}
实现语法 -- 类::new
Lambda 实现实例
1、已知在一个 ArrayList 类中有若干个 Person 对象,讲这些Person对象按照年龄进行降序排序;
public class Exercise{
public static void main(String[] args){
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("张三",12));
list.add(new Person("李四",13));
list.add(new Person("韩梅梅",16));
list.add(new Person("赵四",6));
list.add(new Person("尼古拉斯凯奇",46));
list.add(new Person("Tony",54));
list.sort((o1,o2)-> o2.age-o1.age);
}
}
2、使用TreeSet排序
TreeSer<Person> set = new TreeSer<>((o1,o2)->o2.age-01.age);
set.add(new Person("xiaoming",10));
set.add(new Person("wanggang",11));
set.add(new Person("zhaosi",8));
set.add(new Person("Nigu",43));
set.add(new Person("Tony",22));
set.add(new Person("poily",8));
set.add(new Person("Wngffei",40));
Sout(set);
//运行结果:基本正确,但是只有一个8岁的人;
代码优化
TreeSer<Person> set = new TreeSer<>((o1,o2)->{
if(o1.age >= o2.age){
retrun -1;
}else{
return 1;
}
});
set.add(new Person("xiaoming",10));
set.add(new Person("wanggang",11));
set.add(new Person("zhaosi",8));
set.add(new Person("Nigu",43));
set.add(new Person("Tony",22));
set.add(new Person("poily",8));
set.add(new Person("Wngffei",40));
Sout(set);
//运行结果:正确;
public static void main(String[] args){
ArrayList<Interger> list -= new ArrayList<>();
Collection.addAll(list,1,2,3,4,5,6,7,8,9,0);
list.forEach(ele->{
if(ele%2 == 0)
Sout(ele);
});
}
使用 Lambda 实现线程的实例化
实现线程的实例化;
public static void main(String[] args){
Thread t = new Thread(()->{
for(int i=0;i<100;i++){
Sout(i);
}
});
t.start();
}
系统内置的函数式接口
public static void (String[] args){、
//最常用的是Predicate、Consumer、Function、Supplier
// Predicate<T> : 参数T ·返回值 boolean
// IntPredicate : int-> boolean
// LongPredicate : long->boolean
// DoublePredicate : double
// Consumer<T> : 参数T ·返回值 void
// IntConsumer : int ->void
// LongConsumer
// DoubleConsumer
// Function<T,R> : 参数T ·返回值 R
// IntFuncation : int->R
// LongFunction
// DoubleFunction
// IntToLongFunction: int->long
// IntToDoubleFunction
// LongToIntFunction
// LongToDoubleFunction
// DoubleToIntFunction
// DoubleToLongFunction
// Supplier<T> : 参数无 ·返回值 T
// ……省略好多好多
// UnaryOperator<T> : 参数T ·返回值T
// BinaryOperator : 参数T,T ·返回值T
// BiFunction<T,U,R>: 参数T,U ·返回值R
// BiPredicate<T,U> : 参数T,U ·返回值boolean
// BiConsumer<T,U> : 参数T,U ·返回值void
}
闭包问题
- 什么是
闭包
?- 闭包会提升变量的生命周期,这样就可以获取某一个方法中的局部变量
public static void main(String[] args){
int n = getNum().get();
Sout(n);
//结果是10;
}
private static Supplier<Integer> getNum(){
int num = 10;
return ()->{
return num;
};
}
代码二:
PSVM(String[] args){//public static void main
int a = 10;
Consumer<Integer> c = ele->{
Sout(a+1);
};
c.accept(1);// 结果11
Consumer<Integer> c = ele->{
Sout(a++);
};
c.accept();// 结果报错,因为 闭包 中的变量必须是 final 的(也就是一个常量),所以不能修改这个值
}