《Java8实战》读书笔记--第三章

内容概要

本章内容主要由4部分组成:

  • lamda的使用方法
  • 类型检查、类型推断及限制
  • 方法引用

1. lamda的使用方法

1.1 lamda表达式由三个部分组成

  • 参数列表(语法:(类型 变量名),如果仅有一个变量,基于lambda的类型推断,可省略类型,如:(e)->e.getValue())
  • 箭头
  • lamda主体

1.2 函数式接口

函数式接口是lamda表达式能够应用的地方。所谓函数式接口,即只定义一个抽象方法的接口,比如:
Comparator接口:

public interface Comparator<T>{
	int compare(T o1,T o2);
}

Runnable接口:

public interface Runnable<T>{
	void run();
}

为了更好地标记函数式接口,java提供了一个注解@FunctionalInterface,该注解可以帮助我们在编译时期检查我们的函数式接口是否正确。实际上,我们传入的lamda表达式即函数是接口中抽象方法的实现。

1.3 java8提供的常用函数式接口

接口名称含义使用场景lamda示例
Predicate<T>接受泛型对象T并返回一个booleanboolean表达式(List<String> list)->list.isEmpty()
Consumer<T>接受泛型对象T,没有返回(void)消费一个对象(String s)->System.out.println(s)
Function<T,R>接受泛型对象T,返回泛型对象R从一个对象中提取(String s)->s.length()
Supplier<T>不接受对象,返回泛型对象R创建对象()->new String("hello,world")
BinaryOperator<T,T>传入两个泛型对象T,返回一个泛型对象T合并两个对象(int a,int b)->a+b

这里需要介绍一点:
众所周知,Java的泛型只能绑定到引用类型,那么我们在使用函数式接口的过程当中,难道还要在使用之前对基本类型进行装箱吗?当然不用,java为了方便我们将基本类型应用到lamda当中,提出了类型特化技术,说白了,就是专门提供一系列函数式接口,这些函数式接口的接收参数都不使用泛型,而是使用特定的基本类型,比如IntFunction<R>,该函数式接口就仅接受int型参数,返回泛型R对象。

2.类型检查、类型推断及限制

2.1 类型检查

我们在使用lamda的时候,并没有指定函数式接口,那么lamda是如何工作的?lamda通过上下文获取到它所需要的类型,即目标类型
举个例子:

List<Apple> heavierThan150g=filter(inventory,(Apple a)->a.getWeight()>150);

在这个语句中,lamda主要做了以下工作:

  1. 找到该方法使用的函数式接口的抽象方法:
filter(List<Apple> inventory,Predicate<Apple> p);

可以看到,predicate的参数类型绑定到了Apple,即这段代码中,lamda的目标类型是Apple

  1. 找到 Predicate接口的抽象方法
@FunctionInterface
public interface Predicate<T>{
	boolean test(T t);
}
  1. 根据predicate接口入参和抽象方法的出参对lamda的类型检查提供依据

这个过程即lamda的类型检查。值得一提的是,只要符合目标类型,同一个lamda表达式可以应用在不同的函数式接口中。

2.2 类型推断

lamda表达式还可以通过上下文对传入参数的类型进行类型推断,这就可以进一步简化我们的代码:

Comparator<Apple> c=(Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight());

简化后:

Comparator<Apple> c=(a1,a2)->a1.getWeight().compareTo(a2.getWeight());

2.3 对局部变量的限制

lamda中使用局部变量,需要将局部变量事先声明为final才行,这是lamda以及匿名内部类都会有的对局部变量的限制。究其原因,有两点:

  1. lamda中的操作会在一个单独的线程中完成。局部变量保存在私有的线程栈中,如果允许捕获局部变量,那么lamda就会存在线程不安全的问题;
  2. 作为函数式编程,有一原则就是无side effect,即不允许在函数内部改变外部变量的值。

3. 方法引用

方法引用是java8提供的进一步简化代码的方法,先看个例子:

inventory.sort((a1,a2)->a1.getWeight().compareTo(a2.getWeight()));

=>

inventory.sort(comparing(Apple::getWeight));

方法引用是java8为我们提供的一个新的语法糖,来帮助我们简化我们的lamda代码。
能够使用方法引用的场景有如下三种:

  1. 静态方法
  2. 任意类型的实例方法
  3. 现有对象实例方法

第二点和第三点可能有点难以理解。第二点指的是lamda中参数的方法;第三点指的是lamda外部对象的实例方法。

方法引用中还有个特例,就是构造函数引用,即我们可以通过方法引用的方式来调用某个类的构造函数,比如:

Supplier<Apple> s=()->new Apple();

=>

Supplier<Apple>s=Apple::new;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值