Java8-Lambda表达式和四大函数式接口
Java8-Lambda表达式和四大函数式接口
Java8 在 2014年初发布,在 Java8 中大家讨论最多的特性是 lambda 表达式。
它还有许多重要的功能,像默认方法、Stream API、新的日期时间API。让我们通过示例来了解这些新功能。
一、Lambda 表达式
有许多使用过高级编程语言(比如Scala)的人不知道 lambda 表达式。
在编程中,lambda 表达式(或者函数)只是一个匿名函数,即一个没有名称也没有标识符的函数。它们都被写在
你需要使用的地方,通常作为其他函数的参数。
我们可以把Lambda表达式理解为是一段可以传递的代码
(将代码像数据一样进行传递),其可以代替实现接口中的抽象方法
时的书写匿名内部类的繁琐代码。
lambda 表达式的基本语法是:
either(parameters) -> expressionor(parameters) -> { statements; }or() -> expression
一个典型的 lambda
表达式如下所示:
(x, y) -> x + y //这个函数接受两个参数并返回它们的和。
请注意根据 x
和 y
的类型,方法可能会在多个地方使用。参数可以匹配到 int
类型整数或者字符串类型。
根据上下文,它将两个整数相加或者两个字符串拼接。
实际中举例:
Java中有个Runnable接口,直接使用该接口,需要重写实现其接口内部中的抽象方法。如下:
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println("old run");
}
};
run.run();
12345678
该代码可以使用lambda表达式简化为:
Runnable run1 = () -> System.out.println("lambda run");
run1.run();
1.编写 lambda 表达式的规则
-
lambda表达式可以有零个、一个或多个参数;
-
可以显式声明参数的类型,也可以从上下文推断参数的类型;
-
多个参数必须包含在括号中,并用逗号分隔,空括号用于表示零个参数;
-
当只有一个参数时,如果推断它的类型,可以不使用括号。如
a -> return a * a
-
lambda表达式的函数体可以包含零个,一个或多个语句;
-
如果lambda表达式的函数体只有一行,则可以不用大括号,匿名函数的返回类型与函数体表达式的返回类型
相同。当函数体中大于一行代码则需要用大括号包含;
在《Java8实战》这本书中,对Lambda的解释如下:
可以把Lambda表达式理解为简洁的表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常列表。
2.lambda基础语法
java8中引入了一个新的操作符 ->
,该操作符称为箭头操作符
或Lambda操作符
,该箭头符号将整个Lambda表达式拆分成两部分:
左侧:Lambda表达式的参数列表
,即接口中对应抽象方法的参数列表
。
右侧:Lambda表达式中所需要执行的功能,即Lambda表达式体
。即需要实现的抽象方法
的功能体
。
2.1. 语法格式一:无参数,无返回值
对应格式为: () -> 方法体… 括号内无参数
例如:() -> System.out.println("hello lambda");
@Test
public void test1() {
/* Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("hello runnable");
}
};
r.run();
这么写的话会报提醒:
提示:Anonymous new Runnable() can be replaced with lambda
翻译:匿名新的Runnable()可以替换为lambda
*/
//简化之后就写成:
Runnable r = () -> System.out.println("hello runnable");
r.run();
}
2.2. 语法格式二:有一个参数,无返回值
对应语法格式为 (x) -> 无返回值的方法体
例如: (x) -> System.out.println(x)
若有且只有一个参数
,左侧的小括号可以省略不写
例如: x -> System.out.println(x)
// 有一个参数 , 无返回值
@Test
public void test2(){
List<String> list = new ArrayList<>();
Consumer<String> consumer = (s) -> list.add(s);//将consumer接收到的对象进行具体的功能的消费
consumer.accept("ddd");
consumer.accept("aaa");
consumer.accept("ccc");
list.forEach(System.out::println);
/*
Result:
ddd
aaa
ccc
*/
}
2.3. 语法格式三:有两个或两个以上参数,有返回值
有
两个或两个以上参数
,有返回值
,并且 lambda体中有多条语句
语法为:
(x,y) -> {
方法体
return 返回值
}
多条语句必须使用大括号包括在内,
有返回值,需要使用return 返回返回值.
Comparator<Integer> com = (x, y) -> {
System.out.println("x为"+x);
System.out.println("y为"+y);
return Integer.compare(x,y);
};
如果lambda体中只有一条语句,
那么大括号{}可以省略,
return关键字也可以省略
例如:
Comparator<Integer> com = (x,y) -> {
return Integer.compare(x,y);
}
就可以简写成:
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
Lambda表达式的参数列表的 数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,即'类型推断'.即: (Integer x,Integer y) -> Integer.compare(x,y);简化成:(x,y) -> Integer.compare(x,y);
@Test
public void test3(){
Comparator<Integer> com = (x, y) -> {
System.out.println("x为"+x);
System.out.println("y为"+y);
return Integer.compare(x,y);
};
System.out.println(com.compare(1111,111));
// x为1111
// y为111
// 1
}
利用用Java内置的Comparator接口(比较器)比较两个字符串的长度,可用lambda表达式表示为:使用Lambda表达式直接进行该接口的核心方法体的重写。
//使用Lambda表达式直接进行该接口的核心方法体的重写
Comparator<String> com1 = (x,y) -> {
if(x.length() == y.length()){
return 0;
}else{
if(x.length() > y.length()){
return 1;
}else
return -1;
}
};
System.out.println(com1.compare("aa","aaaaa"));// -1
3.Lambda表达式举例
如何对一个string列表进行排序。我们首先使用Java 8之前的方法来实现:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
静态工具方法Collections.sort接受一个list,和一个Comparator接口作为输入参数,Comparator的实现类可以对输入的list中的元素进行比较。通常情况下,你可以直接用创建匿名Comparator对象,并把它作为参数传递给sort方法。
除了创建匿名对象以外,Java 8 还提供了一种更简洁的方式,Lambda表达式。
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
你可以看到,这段代码就比之前的更加简短和易读。但是,它还可以更加简短:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
只要一行代码,包含了方法体。你甚至可以连大括号对 {} 和return关键字都省略不要。不过这还不是最短的写法:
Collections.sort(names, (a, b) -> b.compareTo(a));
Java编译器能够自动识别参数的类型,所以你就可以省略掉类型不写。
二、函数式接口
- Lambda表达式需要
函数式接口
的支持 - 函数式接口定义: 接口中
只有一个
抽象方法的接口,称为函数式接口。 - 可以使用注解
@FuncitonalInterface
修饰,其修饰作用为:限定
该接口必须为函数式接口
,即该接口中有且只有一个抽象方法。否则无法通过编译。即可以检查是否为函数式接口
。
1. 自定义一个函数式接口:
@FunctionalInterfacepublic interface Operation<T,R> { public R operation(T t1, T t2);}
1.1.方案一:写具体实现方法再直接使用
public void op (Long l1, Long l2, Operation<Long,Long> operation){
System.out.println(operation.operation(l1,l2));
}
@Test
public void testMyOperation(){
op(10l,10l,(x,y) -> x*y);//100
op(100l,200l,(x,y)-> x+y);//300
}
1.2.方案二: 先使用lambda表示具体实现方法体,再进行接口中的方法调用,传入具体值:
@Test
public void testMyOperation(){
Operation<Integer,Integer> op = (x,y) -> x*y;
System.out.println(op.operation(10,10)); //100
}
实际使用时,大多数情况下直接使用
Java8内置四大函数式接口
,并不要进行自己写函数式接口。
三、Java内置的四大核心函数式接口
Consumer<T> 消费型接口 消费对象
void accept(T t);
Supplier<T> 供给型接口 生成对象
T get();
Function<R,T> 函数型接口 指定特定功能
R apply(T t);
Predicate<T> 断言型接口 进行条件判断
boolean test(T t); 12345678
1. 消费型接口
Consumer<T>` `消费型接口`
`void accept(T t);
// Consumer<T> 消费型接口
@Test
public void testConsumer(){
//此时的(d) 小括号里有参数
//原因是因为 Consumer接口有参数
//void accept(T t);
consume(1000,(d)-> System.out.println(d));
}
public void consume(Integer n , Consumer<Integer> con){
//函数接口接收 消费 形参n
con.accept(n);
}
2. 供给型接口
Supplier<T>` `供给型接口
T get();
小括号无参数
// Supplier<T> 供给型接口
@Test
public void testSupplier(){
//T get(); 小括号无参数
List<Integer> numList = getNumList(10,() -> (int)(Math.random()*101));
for ( Integer i: numList
) {
System.out.println(i);
}
}
//调用此方法时,第二个参数提供一个数字集合
public List<Integer> getNumList(int n, Supplier<Integer> sup){
List<Integer> numList = new ArrayList<>();
for (int i = 0; i < n; i++){
numList.add(sup.get()); //通过get方法得到数字 存到numList
}
return numList;
}
3. 函数型接口
Function<R,T>` `函数型接口` `指定特定功能
//Function<R,T> 函数型接口 特定功能
@Test
public void testFunction(){
//将字符串转成大写
String str1 = strHandler("ghslkajh", (s) -> s.toUpperCase());
System.out.println(str1);
}
// Function<R,T> 函数型接口
//定义一个处理字符串功能型接口函数
public String strHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
4. 断言型接口
Predicate<T>` `条件判断
boolean test(T t);` `返回boolean
//断言型接口 Predicate<T>
// boolean test(T t); 返回boolean
@Test
public void testPredicate(){
//返回长度大于3的字符串
List<String> s1 = strFilter(Arrays.asList("huzhiqi", "adaad", "1231", "414441", "gagsgasg"), (s) -> s.length() > 3);
System.out.println(s1); //[huzhiqi, adaad, 1231, 414441, gagsgasg]
//返回包含d的字符串
List<String> s2 = strFilter(Arrays.asList("huzhiqi", "adaad", "1231", "414441", "gagsgasg"), (s) -> s.contains("d"));
System.out.println(s2); // [adaad]
}
//使用断言型接口过滤字符串
public List<String> strFilter(List<String> strs, Predicate<String> pred){
List<String> list = new ArrayList<>();
for (String s:strs
) {
//利用断言型接口进行指定功能判断 即一个功能性条件判断
if(pred.test(s)){
//过滤功能
list.add(s);
}
}
return list;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)