文章目录

  • 1. 为什么使用 Lambda 表达式
  • 2. Lambda 表达式的本质与函数式接口
  • 3. Lambda 表达式的使用
  • (1) 无参,且无返回值
  • (2) 一个参数,无返回值
  • (3) 形参的数据类型可以省略,编译器可以由编译类型推断得出
  • (4) 形参若只需要一个参数时,参数的小括号可以省略
  • (5) 两个或两个以上参数,多条执行语句,并且可以有返回值
  • (6) 当 Lambda体 只有一条语句时,关键字return 和 方法体的大括号都可以省略
  • 小结:Lambda 表达式使用的总结
  • 4. 函数式接口与Lambda表达式结合的示例
  • 4.1 消费型接口的使用
  • 4.2 断定型接口的使用


1. 为什么使用 Lambda 表达式

Lambda 表达式是一个 匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样可以传递)。使用它可以写出更简洁、灵活的代码。

2. Lambda 表达式的本质与函数式接口

Lambda 表达式在 Java 中本质还是一个对象,它针对的是函数式接口,它可以产生函数式接口的一个匿名实现类的实例

所以,以前我们用匿名实现类实现的代码,都可以通过Lambda 表达式来更简单的书写。

能够用Lambda 表达式实现的接口有一定的要求,就是接口中只能有唯一的一个抽象方法。正因为只有唯一的一个抽象方法需要去实现,所以Lambda 表达式不需要我们去指明到底是哪一个方法需要去实现。

而一个接口中,如果只声明了一个抽象方法,则此接口就称为函数式接口

我们举两个例子来展示函数式接口长什么样:

@FunctionalInterface
public interface Runnable {
	/**
	* 此部分摘自Runnable 接口的源码
	* @FunctionalInterface 注解和 @Override 注解类似,只能起到检验是否是函数式接口的作用
	*/
	public abstract void run();
}
@FunctionalInterface
public interface Comparator<T> {
	/**
	* 此部分摘自Comparator 接口的源码
	*/
	int compare(T o1, T o2);
}

我们也可以自己定义一个函数式接口玩一玩:

// @FunctionalInterface 这个注解加不加都不影响MyInterface是一个函数接口的事实
// 不加一样也可以使用我们后面即将讲到的Lambda 表达式
// 此注解 仅仅起到检验的作用
@FunctionalInterface
public interface MyInterface<T> {
	public abstract void method(T o);
}

我们可以在一个函数式接口上使用 java lambda 规范 java lambda实现原理_函数式接口

java.util.function 包下,定义了丰富的函数式接口,注意下图中'函数型接口' 和我们之前一直提到的'函数式接口' 的差异。

java lambda 规范 java lambda实现原理_System_02


java lambda 规范 java lambda实现原理_lambda_03

3. Lambda 表达式的使用

  1. 举例(o1, o2) -> Integer.compare(o1, o2);
  2. 格式
    ->:Lambda操作符 或 箭头操作符
    ->左侧 :Lambda形参列表 (其实就是接口中抽象方法的形参列表)
    ->右侧 :Lambda体 (其实就是重写的抽象方法的方法体)

(1) 无参,且无返回值

// 匿名内部类写法
Runnable r1 = new Runnable() {	// 父类引用指向子类对象
	@Override
	public void run() {
		while (true) {
			System.out.println("线程正在执行中~~~");
		}
	}
};

// 等价于
Runnable r2 = () -> {	// lambda 方法体
	while (true) {
		System.out.println("线程正在执行中~~~");
	}
};

(2) 一个参数,无返回值

// 消费型接口:只接收参数,但是无返回值
Consumer<String> consumer = new Consumer<String>() {	// 父类引用指向子类对象
	@Override
	public void accept(String s) {
		System.out.println(s);
	}
};

// 等价于
Consumer<String> consumer = (String s) -> {
	System.out.println(s);
};

(3) 形参的数据类型可以省略,编译器可以由编译类型推断得出

// 类型推断,可以省略参数
Consumer<String> consumer = (String s) -> {
	System.out.println(s);
};

// 等价于
Consumer<String> consumer = (s) -> {
	System.out.println(s);
};

// 类型推断不仅Lambda表达式中有,讲集合的时候也用到过
ArrayList<String> list = new ArrayList<String>();
// 可以直接写为
ArrayList<String> list = new ArrayList<>();

(4) 形参若只需要一个参数时,参数的小括号可以省略

// 参数唯一时,可以省略小括号
Consumer<String> consumer = (s) -> {
	System.out.println(s);
};
// 等价于
Consumer<String> consumer = s -> {
	System.out.println(s);
};

(5) 两个或两个以上参数,多条执行语句,并且可以有返回值

Comparator<Integer> comparator = new Comparator<Integer>() {
	@Override
	public int compare(Integer o1, Integer o2) {
		System.out.println(o1);
		System.out.println(o2);	// 模拟通用情况,有多条语句
		return o1.compareTo(o2);
	}
};

// 等价于
Comparator<Integer> comparator = (o1, o2) -> {
	System.out.println(o1);
	System.out.println(o2);	// 模拟通用情况,有多条语句
	return o1.compareTo(o2);
};

(6) 当 Lambda体 只有一条语句时,关键字return 和 方法体的大括号都可以省略

// 例子1:
Comparator<Integer> comparator = (o1, o2) -> {
	return o1.compareTo(o2);
};

// 等价于(return和{}要么同时不写,要么同时写)
Comparator<Integer> comparator = (o1, o2) -> o1.compareTo(o2);
// 例子2:
Consumer<String> consumer = s -> {
	System.out.println(s);
};

// 等价于
Consumer<String> consumer = s -> System.out.println(s);

小结:Lambda 表达式使用的总结

-> 左侧:Lambda形参列表的参数类型可以省略(编译类型自动推断);如果Lambda形参列表只有一个参数,其()可以省略;
-> 右侧:Lambda体应该使用一对{}进行包裹;如果Lambda体只有一条执行语句(可能是return语句),可以省略{}return关键字。

4. 函数式接口与Lambda表达式结合的示例

4.1 消费型接口的使用

public class Main {
	@Test
	public void testOldWay() {	// 匿名实现类的方式
		spendMoney(500, new Consumer<Double>() {
			@Override
			public void accept(Double money) {
				System.out.println("消费了 " + money + " 元。");
			}
		});
	}

	@Test
	public void testNewWay() { // 使用Lambda 表达式的方式
		spendMoney(500, money -> System.out.println("消费了 " + money + " 元。"));
	}
	
	public void spendMoney(double money, Consumer<Double> consumer) {
		consumer.accept(money);
	}
}

4.2 断定型接口的使用

public class Main {
	private List<String> list = Arrays.asList("北京", "南京", "天津", "吴京", "普京");
	@Test
	public void testOldWay() {	// 传统匿名实现类的方式,筛选含"京"的字符串
		List<String> newStrs = filterString(list, new Predicate<String>() {
			@Override
			public boolean test(String s) {
				return s.contains("京");
			}
		});
	}

	@Test
	public void testNewWay() {	// 使用Lambda 表达式的方式,筛选含"京"的字符串
		List<String> newStrs = filterString(list, s -> s.contains("京"));
	}
	
	// 根据传入的规则,过滤集合中的字符串。此规则由Predicate接口的实现类决定
	public List<String> filterString(List<String> list, Predicate<String> predicate) {
		ArrayList<String> newList = new ArrayList<>();
		for (String s : list) {
			if (predicate.test(s)) {
				newList.add(s);
			}
		}
		return newList;
	}
}