1、Lambda表达式简介
Lambda表达式(也称闭包),可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。Lambda表达式可以简化函数式接口的使用。函数式接口就是一个只有一个抽象方法的普通接口,像这样的接口就可以使用Lambda表达式来简化代码的编写。
2、Lambda的基本语法
(parameters) -> expression;
或者
(parameters) -> { statements; }
箭头(->)操作符将 Lambda 表达式拆分成两部分:
左侧(parameters):Lambda 表达式的参数列表
右侧:Lambda 表达式中所需实现的功能, 即 Lambda 体
注意:
- 可选参数类型声明: 不需要声明参数类型,编译器可以统一识别参数值
- 可选的参数圆括号: 一个参数无需定义圆括号,但多个参数需要定义圆括号
- 可选的Lambda体大括号:如果主体包含了一个语句,就不需要使用大括号
- 可选的返回关键字: 如果Lambda体不加{ }就不用写return,Lambda体加上{ }就需要添加return。
@Test
public void lambdaTest(){
Integer num = (int) (Math.random() * 100);
functionInterface<Integer> functionInterface = x -> x > 5? "good" :"bad";
String test = functionInterface.functionTest(num);
System.out.println(num + " is " + test);
}
interface functionInterface<T>{
String functionTest(T t);
}
3、Lambda的适用场景
Lambda表达式只适用于函数式接口,即接口有且只有一个抽象方法。
函数式接口
- 有且只有一个抽象方法的接口被为函数式接口
- 接口现在还可以拥有默认方法(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法)。哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口
- 函数式接口可以显式的被@FunctionalInterface所表示,当被标识的接口不满足规定时,编译器会提示报错,注意@FunctionalInterface不是必需的,但对于为此设计的接口而言,使用它是比较好的做法
4、 Java8内置的函数式接口
Java 8的库设计师帮你在java.util.function包中引入了几个新的函数式接口。例如:Predicate、Consumer和Function
接口 | 参数 | 返回值 |
---|---|---|
Consumer(消费型接口) | T | void |
Supplier(供给型接口) | none | T |
Function(函数型接口) | T | R |
Predicate(断言型接口) | T | boolean |
java.util.function.Consumer < T > 接口定义了一个名叫accept的抽象方法,它接受泛型T对象,无返回值,重点在于内部消费.
@Test
public void test1(){
//消费型 Consumer 接口只有一个抽象方法 accept,参数列表只有一个泛型t,无返回值,重点在于内部消费
consumerTest("good", System.out::println);
}
public void consumerTest(String str, Consumer<String> consumer){
consumer.accept(str);
}
java.util.function.Supplier< T > 接口定义了一个名叫get的抽象方法,参数列表为空,有返回值,返回值得数据类型为T
@Test
public void test2(){
//供给型 Supplier 只有一个抽象方法 get,参数列表为空,有返回值,返回值得数据类型为T。
List<Integer> integers = supplierTest(5, () -> (int) (Math.random() * 100));
integers.forEach(System.out::println);
}
public List<Integer> supplierTest(Integer num, Supplier<Integer> supplier){
Integer[] arr = new Integer[num];
for (int i = 0; i < num; i++) {
arr[i] = supplier.get();
}
return Arrays.asList(arr);
}
java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。如果你需要定义一个Lambda,将输入对象的信息映射到输出,就可以使用这个接口
@Test
public void test3(){
//函数型 Function<T, R> 只有一个抽象方法名为 apply,参数列表只有一个参数为T,有返回值,返回值的数据类型为R。
String test = functionTest("Hello Mr.Zhu", String::toUpperCase);
System.out.println(test);
}
public String functionTest(String str,Function<String,String> function){
return function.apply(str);
}
java.util.function.Predicate< T >接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。需要表示一个涉及类型T的布尔表达式时,就可以使用这个接口。
@Test
public void test4(){
//断言型 又名判断型。 Predicate 只有一个抽象方法 test,参数列表只有一个参数为 T,有返回值,返回值类型为 boolean。
boolean test = predicateTest((int) (Math.random() * 100), x -> {
System.out.println(x);
return x > 50 ? true : false;
});
System.out.println(test);
}
public boolean predicateTest(Integer num , Predicate<Integer> predicate){
return predicate.test(num);
}
5、方法引用
方法引用是用来直接访问类或者实例已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。
方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。事实上,方法引用就是让你根据已有的方法实现来创建Lambda表达式。但是,显式地指明方法的名称,你的代码的可读性会更好
5.1、如何构建方法引用
使用::操作符将方法名和对象或类的名字分隔开。::是域操作符(也可以称作定界符、分隔符。
方法引用主要有三类。
- 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)
- 指向任意类型实例方法的方法引用(例如String的length方法,写作String::length)。引用一个对象的方法,而这个对象本身是Lambda的一个参数。例如,Lambda表达式(String s)-> s.toUppeCase()可以写作String::toUpperCase。
- 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensive-Transaction::getValue)。
5.2、构造函数引用
对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用:ClassName::new
@Test
public void test(){
Function<String, Person> function = Person::new // 等同于 Function<String,Person> function1 = (String s) -> new Person(s);
Person person = function.apply("thinkwon");
// doge
System.out.println(person.getName());
}
class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}