Java8新特性-Lambda表达式与函数式接口

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);
}

我们可以在一个函数式接口上使用 @ F u n c t i o n a l I n t e r f a c e @FunctionalInterface @FunctionalInterface 注解,这样做可以检验它是否是一个函数式接口。同时,javadoc也会包含一条声明,说明该接口是一个函数式接口。

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

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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁头娃撞碎南墙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值