Lambda 表达式
简介
- Lambda 表达式是一个匿名函数,即没有函数名的函数。
- 在 Java 8 之前,编写一个匿名内部类的代码很冗长、可读性很差。
- Lambda 表达式的应用则使代码变得更加紧凑,可读性增强;另外,Lambda 表达式使并行操作大集合变得很方便,可以充分发挥多核 CPU 的优势,更易于为多核处理器编写代码。
语法
- 一个括号内用逗号分隔的形式参数列表:实际上就是接口里面抽象方法的参数;
- 一个箭头符号:
->
,这个箭头我们又称为 Lambda 操作符或箭头操作符; - 方法体:可以是表达式和代码块,是重写的方法的方法体。
方法体为表达式,该表达式的值作为返回值返回。
//语法
(parameters) -> expression
//例子,返回两数相加的值
(num1,num2) -> num1+num2
方法体为代码块,必须用 {} 来包裹起来,且需要一个 return 返回值,但若函数式接口里面方法返回值是 void,则无需返回值。
//语法
(parameters) -> {
statement1;
statement2;
return value;
}
//例子,分别打印两个数,不返回
(num1,num2) -> {
System.out.println(num1);
System.out.println(num2);
}
//例子,分别打印两个数,返回相加值
(num1,num2) -> {
System.out.println(num1);
System.out.println(num2);
return num1+num2;
}
实例
无参数无返回值
无参数无返回值,指的是接口实现类重写的方法是无参数无返回值的, Runnable 接口匿名内部类就属于此类:
public class LambdaDemo2 {
public static void main(String[] args) {
// 通过匿名内部类实例实例化一个 Runnable 接口的实现类
Runnable runnable1 = new Runnable() {
@Override
public void run() { // 方法无形参列表,也无返回值
System.out.println("Hello, 匿名内部类");
}
};
// 执行匿名内部类的 run() 方法
runnable1.run();
// 无参数无返回值,通过 lambda 表达式来实例化 Runnable 接口的实现类
Runnable runnable2 = () -> System.out.println("Hello, Lambda");
// 执行通过 lambda 表达式实例化的对象下的 run() 方法
runnable2.run();
}
}
运行结果:
Hello, 匿名内部类
Hello, Lambda
单参数无返回值
无参数无返回值,指的是接口实现类重写的方法是单个参数,返回值为 void 的,实例如下:
import java.util.function.Consumer;
public class LambdaDemo3 {
public static void main(String[] args) {
// 单参数无返回值
Consumer<String> consumer1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer1.accept("Hello World!");
Consumer<String> consumer2 = (String s) -> {
System.out.println(s);
};
consumer2.accept("你好,世界!");
}
}
运行结果:
Hello World!
你好,世界!
省略数据类型
什么叫省略数据类型呢?我们依旧采用上面的实例,使用 Lambda 表达式可以省略掉括号中的类型,实例代码如下:
// 省略类型前代码
Consumer<String> consumer2 = (String s) -> {
System.out.println(s);
};
consumer2.accept("你好,世界!");
// 省略类型后代码
Consumer<String> consumer3 = (s) -> {
System.out.println(s);
};
consumer3.accept("你好,世界!");
Tips:之所以能够省略括号中的数据类型,是因为我们在Comsumer<String>
处已经指定了泛型,编译器可以推断出类型,后面就不用指定具体类型了。称为类型推断。
省略参数的小括号
当我们写的 Lambda 表达式只需要一个参数时,参数的小括号就可以省略,改写上面实例的代码:
// 省略小括号前代码
Consumer<String> consumer3 = (s) -> {
System.out.println(s);
};
consumer3.accept("你好,世界!");
// 省略小括号后代码
Consumer<String> consumer4 = s -> {
System.out.println(s);
};
consumer3.accept("你好,世界!");
观察实例代码,之前的 (s) -> 可以改写成 s ->,这样写也是合法的。
省略 return 和大括号
当 Lambda 表达式体只有一条语句时,如果有返回,则 return 和大括号都可以省略,实例代码如下:
import java.util.Comparator;
public class LambdaDemo4 {
public static void main(String[] args) {
// 省略 return 和 {} 前代码
Comparator<Integer> comparator1 = (o1, o2) -> {
return o1.compareTo(o2);
};
System.out.println(comparator1.compare(12, 12));
// 省略 return 和 {} 后代码
Comparator<Integer> comparator2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator2.compare(12, 23));
}
}
运行结果:
0
-1
方法引用
简介
方法引用(Method References)是一种语法糖,它本质上就是 Lambda 表达式,我们知道Lambda表达式是函数式接口的实例,所以说方法引用也是函数式接口的实例。
语法
方法引用使用一对冒号(::
)来引用方法,格式如下:
类或对象 :: 方法名
//例子
System.out::println
使用场景和使用条件
方法引用的使用场景为:当要传递给Lambda体的操作,已经有实现的方法了,就可以使用方法引用。
方法引用的使用条件为:接口中的抽象方法的形参列表和返回值类型与方法引用的方法形参列表和返回值相同。
方法引用的分类
对于方法引用的使用,通常可以分为以下 4 种情况:
- 对象 :: 非静态方法:对象引用非静态方法,即实例方法;
- 类 :: 静态方法:类引用静态方法;
- 类 :: 非静态方法:类引用非静态方法;
- 类 :: new:构造方法引用。
对象引用实例方法
对象引用实例方法,我们已经在上面介绍过了,System.out
就是对象,而println
就是实例方法,这里不再赘述。
类引用静态方法
类引用静态方法,请查看以下实例:
import java.util.Comparator;
import java.util.function.Function;
public class MethodReferencesDemo3 {
public static void main(String[] args) {
// 使用 Lambda 表达式
Function<Double, Long> function1 = d -> Math.round(d);
Long apply1 = function1.apply(1.0);
System.out.println(apply1);
// 使用方法引用,类 :: 静态方法( round() 为静态方法)
Function<Double, Long> function2 = Math::round;
Long apply2 = function2.apply(2.0);
System.out.println(apply2);
}
}
运行结果:
1
2
类引用实例方法
类引用实例方法,比较难以理解,请查看以下实例:
import java.util.Comparator;
public class MethodReferencesDemo4 {
public static void main(String[] args) {
// 使用 Lambda 表达式
Comparator<String> comparator1 = (s1, s2) -> s1.compareTo(s2);
int compare1 = comparator1.compare("Hello", "Java");
System.out.println(compare1);
// 使用方法引用,类 :: 实例方法( compareTo() 为实例方法)
Comparator<String> comparator2 = String::compareTo;
int compare2 = comparator2.compare("Hello", "Hello");
System.out.println(compare2);
}
}
运行结果:
-2
0
类引用构造方法
类引用构造方法,可以直接使用类名 :: new,请查看如下实例:
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferencesDemo5 {
static class Person {
private String name;
public Person() {
System.out.println("无参数构造方法执行了");
}
public Person(String name) {
System.out.println("单参数构造方法执行了");
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
// 使用 Lambda 表达式,调用无参构造方法
Supplier<Person> supplier1 = () -> new Person();
supplier1.get();
// 使用方法引用,引用无参构造方法
Supplier<Person> supplier2 = Person::new;
supplier2.get();
// 使用 Lambda 表达式,调用单参构造方法
Function<String, Person> function1 = name -> new Person(name);
Person person1 = function1.apply("小慕");
System.out.println(person1.getName());
// 使用方法引用,引用单参构造方法
Function<String, Person> function2 = Person::new;
Person person2 = function1.apply("小明");
System.out.println(person2.getName());
}
}
运行结果:
无参数构造方法执行了
单参数构造方法执行了
小慕
单参数构造方法执行了
小明
在实例中,我们使用了Lambda表达式和方法引用两种方式,分别调用了静态内部类Person的无参和单参构造方法。函数式接口中的抽象方法的形参列表与构造方法的形参列表一致,抽象方法的返回值类型就是构造方法所属的类型。