一、简述
Lambda 表达式,也可称为闭包,它
允许把函数作为一个方法的参数 (函
数作为参数传递进方法中)
Lambda 简化了匿名内部类的形式,
可以达到同样的效果,匿名内部类在
编译之后会创建一个新的匿名内部类
出来,而 Lambda 是调用
JVM invokedynamic 指令实现的,并
不会产生新类
Lambda表达式返回的是接口对象实例
(2) 基本语法:
(参数列表) → {
方法体
};
→ 是 Lambda 运算符,英文名是
goes to
二、Lambda 表达式语法
6 种情况:
public class Test {
public static void main(String[] args) {
I01 i01 = () -> {
System.out.println("无返回值、无参数");
};
I02 i02 = (int a) -> {
System.out.println("无返回值,单个参数。a=" + a);
};
I03 i03 = (int a, int b) -> {
System.out.println("无返回值,多个参数。a=" + a + ",b=" + b);
};
I04 i04 = () -> {
System.out.println("有返回值、无参数");
return 4;
};
I05 i05 = (int a) -> {
System.out.println("有返回值,单个参数。a=" + a);
return 5;
};
I06 i06 = (int a, int b) -> {
System.out.println("有返回值,多个参数。a=" + a + ",b=" + b);
return 6;
};
i01.method();
i02.method(5);
i03.method(5,10);
System.out.println(i04.method());
System.out.println(i05.method(5));
System.out.println(i06.method(5, 10));
}
}
interface I01 {
void method();
}
interface I02 {
void method(int a);
}
interface I03 {
void method(int a, int b);
}
interface I04 {
int method();
}
interface I05 {
int method(int a);
}
interface I06 {
int method(int a, int b);
}
输出:
无返回值、无参数
无返回值,单个参数。a=5
无返回值,多个参数。a=5,b=10
有返回值、无参数
4
有返回值,单个参数。a=5
5
有返回值,多个参数。a=5,b=10
6
三、函数式接口 (Functional Interface)
Java 8 为了使现有的函数更加友好地
支持 Lambda 表达式,引入了函数式
接口的概念
函数式接口本质上是一个仅有一个抽
象方法的普通接口,所以又叫 SAM 接
口 (Single Abstract Method Interface)
函数式接口在实际使用过程中很容易
出错,比如某人在接口定义中又增加
了另一个方法,则该接口不再是函数
式接口,此时将该接口转换为 Lambda
表达式会报错。为了克服函数式接口
的脆弱性,并且能够明确声明接口是
作为函数式接口的意图,Java 8增加
了**@FunctionalInterface**注解来标
注函数式接口。
使用@FunctionalInterface注解标注的
接口必须是函数式接口,也就是说该
接口中只能声明一个抽象方法,如果
声明多个抽象方法就会报错。但是默
认方法和静态方法不属于抽象方法,
因此在函数式接口中也可以定义默认
方法和静态方法
@FunctionalInterface
interface InterfaceDemo {
void method(int a);
static void staticMethod() {
...
}
default void defaultMethod() {
...
}
}
四、Lambda 表达式精简语法
① 参数类型可以省略
I02 i02 = (
int
a) -> {System.out.println(...);};
I02 i02 = (a) -> {System.out.println(...);};
② 假如只有一个参数,那么()
括号
可以省略
I02 i02 =
(
a
)
-> {System.out.println(...);};
I02 i02 = a -> {System.out.println(...);};
③ 假如方法体只有一条语句,那么语
句后的;
分号和方法体的{}
大括号可
以一起省略
I02 i02 = a ->
{
System.out.println(...)
;}
;
I02 i02 = a -> System.out.println(...);
④ 如果方法体中唯一的语句是 return
返回语句,那么在省略第3种情况的
同时,return
也必须一起省略
I05 i05 = a ->
{return
1
;}
;
I05 i05 = a -> 1;
五、方法引用(Method Reference)
在Java 8中可以用方法引用来进一
步简化 Lambda 表达式
有时候多个 Lambda 表达式的实现
函数是一样的,我们可以封装成一
个通用方法,再通过方法引用来实
现接口
1. 方法引用语法
① 如果是实例方法:
对象名::实例方法名
② 如果是静态方法:
类名::实例方法名
③ 如果是构造方法:
类名::new
2. 实例方法引用
public class Test {
public void eat(int a) {
System.out.println("吃东西。" + "a=" + a);
}
public static void main(String[] args) {
//Lambda表达式写法:
Dog dog1 = (a) -> System.out.println("吃东西。" + "a=" + a);
Cat cat1 = (a) -> System.out.println("吃东西。" + "a=" + a);
dog1.doSomething(5);
cat1.doSomething(5);
//方法引用写法:
Test test = new Test();
Dog dog2 = test::eat;
Cat cat2 = test::eat;
dog2.doSomething(10);
cat2.doSomething(10);
}
}
@FunctionalInterface
interface Dog {
void doSomething(int a);
}
@FunctionalInterface
interface Cat {
void doSomething(int a);
}
3. 构造方法引用
如果函数式接口的实现恰好可以通过
调用一个类的构造方法来实现 (比如
说接口方法与这个构造方法的参数个
数、参数类型和返回值都对的上),那
么就可以使用构造方法引用
public class Test {
public void eat(int a) {
System.out.println("吃东西。" + "a=" + a);
}
public static void main(String[] args) {
//Lambda表达式写法:
DogService dogService1 = (name, age) -> new Dog(name, age);
System.out.println(dogService1.getDog("大狗", 5));
//方法引用写法:
DogService dogService2 = Dog::new;
System.out.println(dogService2.getDog("二狗", 3));
}
}
@FunctionalInterface
interface DogService {
Dog getDog(String name, int age);
}
class Dog {
String name;
int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
六、内置函数式接口
java.util.function 包下有一系列内
置函数式接口,也是 Java 8 伴随
Lambda 表达式一起推出的
/*
* Java8 内置的四大核心函数式接口
*
* Consumer<T> : 消费型接口
* void accept(T t);
*
* Supplier<T> : 供给型接口
* T get();
*
* Function<T, R> : 函数型接口
* R apply(T t);
*
* Predicate<T> : 断言型接口
* boolean test(T t);
*
*/
public class TestLambda3 {
//Predicate<T> 断言型接口:
@Test
public void test4(){
List<String> list = Arrays.asList("Hello", "atguigu", "Lambda", "www", "ok");
List<String> strList = filterStr(list, (s) -> s.length() > 3);
for (String str : strList) {
System.out.println(str);
}
}
//需求:将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for (String str : list) {
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
//Function<T, R> 函数型接口:
@Test
public void test3(){
String newStr = strHandler("\t\t\t 我大尚硅谷威武 ", (str) -> str.trim());
System.out.println(newStr);
String subStr = strHandler("我大尚硅谷威武", (str) -> str.substring(2, 5));
System.out.println(subStr);
}
//需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun){
return fun.apply(str);
}
//Supplier<T> 供给型接口 :
@Test
public void test2(){
List<Integer> numList = getNumList(10, () -> (int)(Math.random() * 100));
for (Integer num : numList) {
System.out.println(num);
}
}
//需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
//Consumer<T> 消费型接口 :
@Test
public void test1(){
happy(10000, (m) -> System.out.println("每次消费:" + m + "元"));
}
public void happy(double money, Consumer<Double> con){
con.accept(money);
}
}