1、Lambda表达式的简介
Java8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。
Lambda 表达式(Lambda Expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的Lambda抽象(Lambda Abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
【示例】下面这个例子是使用Lambda表达式的简单示例,代码不仅简洁,而且还可读。
/**
* Lambda表达式的使用
* @author pan_junbiao
*/
@Test
public void simpleTest()
{
//1、使用传统方式创建线程
Thread thread1 = new Thread(new Runnable()
{
//重写run()方法
@Override
public void run()
{
System.out.println("线程启动(使用传统方式)");
}
});
thread1.start();
//2、使用Lambda表达式创建线程
Thread thread2 = new Thread(()->System.out.println("线程启动(使用Lambda表达式)"));
thread2.start();
}
执行结果:
2、Lambda表达式的语法
Lambda表达式的语法如下:
() -> {}
Lambda的语法定义,它由三部分组成:
参数列表:Lambda表达式的参数列表,参数的类型可以省略,因为编译器会根据方法体中的参数来确定类型。
操作符(->):Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或Lambda操作符。
代码块:Lambda表达式中所需执行的功能, 即Lambda方法体,如果代码块只有一条语句时可以将“{}”省略;如果只有一条return语句,那么“return”可以省略。
Lambda表达式本质上是一个匿名方法。让我们来看下面这个例子:
public int add(int x, int y) {
return x + y;
}
转成Lambda表达式:
(int x, int y) -> x + y;
参数类型也可以省略,因为编译器会根据方法体中的参数来确定类型:
(x, y) -> x + y;
或者:
(x, y) -> { return x + y; } //显式指明返回值
2.1 无参数,无返回值
/**
* 无参数,无返回值
* @author pan_junbiao
*/
@Test
public void test1()
{
Runnable runnable = ()->System.out.println("线程启动");
runnable.run();
}
执行结果:
2.2 有参数,无返回值
/**
* 有参数,无返回值
* @author pan_junbiao
*/
@Test
public void test2()
{
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("您好,欢迎访问 pan_junbiao的博客");
}
执行结果:
2.3 无参数,有返回值
/**
* 无参数,有返回值
* @author pan_junbiao
*/
@Test
public void test3()
{
Supplier<String> supplier = ()->"您好,欢迎访问 pan_junbiao的博客".substring(8, 22);
System.out.println(supplier.get());
}
执行结果:
2.4 有一个参数,有返回值
/**
* 有一个参数,有返回值
* @author pan_junbiao
*/
@Test
public void test4()
{
Function<String, String> function = (s)->s.substring(8, 22);
System.out.println(function.apply("您好,欢迎访问 pan_junbiao的博客"));
}
执行结果:
2.5 有两个参数,有返回值
/**
* 有多个参数,有返回值
* @author pan_junbiao
*/
@Test
public void test5()
{
BiFunction<String,String,String> biFunction = (String s1, String s2) -> s1.concat(s2);
System.out.println(biFunction.apply("您好,欢迎访问 ","pan_junbiao的博客"));
}
执行结果:
3、函数式接口
Lambda表达式的目标类型必须是“函数式接口(functional interface)”。函数式接口:只包含一个抽象方法的接口。接口可以包含多个默认方法、类方法,但关键是一个抽象方法。所谓函数式接口,指的是只有一个抽象方法的接口。函数式接口可以被隐式转换为Lambda表达式。函数式接口可以用@FunctionalInterface注解标识。
3.1 自定义函数式接口
Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。@FunctionalInterface 就是用来指定某个接口必须是函数式接口,所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。
【示例】使用@FunctionalInterface注解自定义函数式接口,要求接受两个参数,并且有返回值。
(1)创建名称为MyFunction的函数式接口。
/**
* 自定义函数式接口
* @author pan_junbiao
**/
@FunctionalInterface
public interface MyFunction<T, U, R>
{
R apply(T t, U u);
}
(2)使用函数式接口实现数字的加法、减法、乘法、除法、字符串拼接。
/**
* 使用自定义函数式接口
* @author pan_junbiao
*/
@Test
public void MyFunctionTest()
{
//加法
MyFunction<Integer,Integer,Integer> addFunction1 = (Integer x,Integer y) -> x+y;
System.out.println("加法执行结果:" + addFunction1.apply(3,5));
//减法
MyFunction<Integer,Integer,Integer> subFunction1 = (Integer x,Integer y) -> x-y;
System.out.println("减法执行结果:" + subFunction1.apply(10,4));
//乘法
MyFunction<Integer,Integer,Integer> mulFunction1 = (Integer x,Integer y) -> x*y;
System.out.println("乘法执行结果:" + mulFunction1.apply(20,5));
//除法
MyFunction<Integer,Integer,Integer> divFunction1 = (Integer x,Integer y) -> {
//被除数不能为0
if(y==0)
{
return 0;
}
return x/y;
};
System.out.println("除法执行结果:" + divFunction1.apply(20,4));
//字符串拼接
MyFunction<String,String,String> strFunction = (String s1, String s2) -> s1.concat(s2);
System.out.println("字符串拼接结果:" +strFunction.apply("您好,欢迎访问 ","pan_junbiao的博客"));
}
执行结果:
3.2 Java内置的函数式接口
Java已经提供了一些函数式接口,这些接口在 java.util.function 包下,常用的函数式接口如下:
函数式接口 | 参数类型 | 返回类型 | 说明 |
Consumer<T> | T | 无 | 接受一个参数T,无返回结果。 |
Supplier<T> | 无 | T | 不接受任何参数,返回结果T。 |
Function<T, R> | T | R | 接受一个参数T,返回结果R。 |
Predicate<T> | T | boolean | 接受一个参数T,返回结果boolean类型。 |
UnaryOperator<T> | T | T | 继承自Function<T, T>,接受一个参数T,返回相同类型T的结果。 |
BiFunction<T, U, R> | T, U | R | 接受两个参数T和U,返回结果R。 |
BinaryOperator<T> | T | T | 继承自BiFunction<T, T, T>,接受两个相同类型T的参数,返回相同类型T的结果。 |
【示例】使用Consumer<T>、Supplier<T>、Function<T, R>、Predicate<T>内置的函数式接口。
/**
* 使用内置的函数式接口
* @author pan_junbiao
*/
@Test
public void FunctionalInterfaceTest()
{
Consumer<String> consumer = (s) -> System.out.println(s);
consumer.accept("您好,欢迎访问 pan_junbiao的博客");
Supplier<String> supplier = () -> "您好,欢迎访问 pan_junbiao的博客".substring(8, 22);
System.out.println(supplier.get());
Function<String, String> function = (s) -> s.substring(8, 22);
System.out.println(function.apply("您好,欢迎访问 pan_junbiao的博客"));
Predicate<String> predicate = (s) -> {
String userName = "pan_junbiao的博客";
return userName.equals(s);
};
System.out.println(predicate.test("pan_junbiao的博客"));
}
执行结果:
4、方法引用与构造器引用(::操作符)
方法引用和构造器引用可以让Lambda表达式的代码在简洁的基础上再简洁,使用::双冒号来使用方法引用,看看有几种引用方式:
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
//静态方法引用
Comparator<Integer> c1 = (x, y) -> Integer.compare(x, y);
Comparator<Integer> c2 = Integer::compare;
//实例方法引用1
userList.forEach(user -> System.out.println(user));
userList.forEach(System.out::println);
//实例方法引用2
userList.forEach(user -> user.getName());
userList.forEach(User::getName);
//构造器引用
strList.stream().map(s -> new String(s));
strList.stream().map(String::new);
【示例】遍历用户列表。
/**
* 遍历用户列表
* @author pan_junbiao
*/
@Test
public void forEachTest()
{
//创建用户列表
List<String> userList = Arrays.asList("pan_junbiao","阿标","pan_junbiao的博客","KevinPan");
//遍历用户信息
userList.forEach(System.out::println);
//上述语句等同于如下:
//userList.forEach(user -> System.out.println(user));
}
执行结果: