Lambda表达式基础学习
一、Lambda表达式
1、Lambda的基本组成
Lambda表达式由三部分组成:参数列表、箭头、Lambda主体
从上图中为例:
参数列表:采用了Comparator中compare方法的参数,两个Apple。
箭头: -> 连接参数列表和Lambda主体
Lambda主体:比较两个Apple的重量,表达式就是返回值
2、Lambda的基本语法
(parameters)-> expression
或者
(parameters) -> { statements; }
例子:
布尔表达式 (List list) -> list.isEmpty()
创建对象 () -> new Apple(10)
消费一个对象 (Apple a) -> { System.out.println(a.getWeight()); }
从一个对象中选择/抽取 (String s) -> s.length()
组合两个值 (int a, int b) -> a * b
比较两个对象 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
3、函数式接口
函数式接口:只定义一个抽象方法的接口。
public interface Predicate<T>{
boolean test(T t);
}
函数接口的运用:Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法的实现,并把整个表达式作为函数式接口的实例。
函数描述符:函数式接口抽象方法的签名基本上就是Lambda表达的式签名。
()-> System.out.println(" hello world ! ")
在上述的例子中对应的签名就是 ()->void
4、Lambda实践(环绕模式)
环绕执行模式:资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源。这个设置和清理阶段总是很类似,并且会围绕着执行处理的那些重要代码。这就是所谓的环绕执行(execute around)模式
public static String processFile() throws IOException {
try (BufferedReader br =
new BufferedReader(new FileReader("data.txt"))) {
return br.readLine();
}
}
4.1 行为参数化
从上面得代码中,返回得只是读取文件得第一行,那么如果我想要读取头两行呢?或者式其他得呢。
那么可以通过传递行为来进行读取,这正式Lambda的拿手好戏。
String result = processFile((BufferedReader br) -> br.readLine() + br.readLine());
4.2 使用函数式接口来传递行为
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;
}
4.3 执行第一个行为
public static String processFile(BufferedReaderProcessor p) throws
IOException {
try (BufferedReader br =
new BufferedReader(new FileReader("data.txt"))) {
return p.process(br);
}
}
4.4 传递Lambda
//处理一行
String oneLine = processFile((BufferedReader br) -> br.readLine());
//处理两行
String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());
5、API中的函数式接口介绍
5.1 Predicate
java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个
boolean
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
static List<Apple> filterApples(List<Apple> inventory,Predicate<Apple> p){
List<Apple> list = new ArrayList<>();
for (Apple apple : inventory){
if(p.test(apple)){
list.add(apple);
}
}
return list;
}
这个就不需要自己去定义,直接使用就好了
5.2 Consumer
java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。
@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
}
public static <T> void forEach(List<T> list, Consumer<T> c){
for(T i: list){
c.accept(i);
}
}
forEach( Arrays.asList(1,2,3,4,5), (Integer i) -> System.out.println(i));
5.3 Function
java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。
//将一个String列表映射到包含每个String长度的Integer列表
@FunctionalInterface
public interface Function<T, R>{
R apply(T t);
}
public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
List<R> result = new ArrayList<>();
for(T s: list){
result.add(f.apply(s));
}
return result;
}
// [7, 2, 6]
List<Integer> l = map(
Arrays.asList("lambdas","in","action"),
(String s) -> s.length()
);
Java 8中的常用函数式接口
函数式接口 函数描述符 原始类型特化
Predicate<T> T->boolean IntPredicate,LongPredicate, DoublePredicate
Consumer<T> T->void IntConsumer,LongConsumer, DoubleConsumer
Function<T,R> T->R IntFunction<R>,
IntToDoubleFunction,
IntToLongFunction,
LongFunction<R>,
LongToDoubleFunction,
LongToIntFunction,
DoubleFunction<R>,
ToIntFunction<T>,
ToDoubleFunction<T>,
ToLongFunction<T>,
Supplier<T> ()->T BooleanSupplier,IntSupplier, LongSupplier, DoubleSupplier
UnaryOperator<T> T->T IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
BinaryOperator<T> (T,T)->T IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
BiPredicate<L,R> (L,R)->boolean
BiConsumer<T,U> (T,U)->void ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T>
BiFunction<T,U,R> (T,U)->R ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U>
Lambdas及函数式接口的例子
使用案例 Lambda 的例子 对应的函数式接口
布尔表达式 (List<String> list) -> list.isEmpty() Predicate<List<String>>
创建对象 () -> new Apple(10) Supplier<Apple>
消费一个对象 (Apple a) -> System.out.println(a.getWeight()) Consumer<Apple>
从一个对象中 (String s) -> s.length() Function<String, Integer>或ToIntFunction<String>
选择/提取
合并两个值 (int a, int b) -> a * b IntBinaryOperator
比较两个对象 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) Comparator<Apple>或BiFunction<Apple, Apple, Integer>
或 ToIntBiFunction<Apple, Apple>
5.4 异常、Lambda、函数式接口
异常:
任何函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda表达式来抛出异常,
有两种办法:定义一个自己的函数式接口,并声明受检异常,或者把Lambda包在一个try/catch块中
第一种:显示声明
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;
}
第二种:在lambda中使用try/catch
Function<BufferedReader, String> f = (BufferedReader b) -> {
try {
return b.readLine();
}
catch(IOException e) {
throw new RuntimeException(e);
}
};
6、类型检查、类型推断以及限制
6.1 类型检查
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
public static List<Apple> filterApples(List<Apple> inventory,Predicate<Apple> p){
List<Apple> list = new ArrayList<>();
for (Apple apple : inventory){
if(p.test(apple)){
list.add(apple);
}
}
return list;
}
List<Apple> list1 = filterApples(apples,
(Apple a) -> a.getWeight() > 150);
第一步:找出filter方法声明
第二步:要求是Predicate p 对象的正式参数
第三步: 函数式接口Predicate 定义了一个test方法,返回类型式boolean
第四步:test()方法描述了一个函数描述符,它可以接受一个参数Apple,返回一个boolean类型
第五步:filter的任何实际参数都要匹配这个要求
6.2 同一个Lambda表达式,可以用不通的函数接口来表示
Comparator<Apple> c1 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
ToIntBiFunction<Apple, Apple> c2 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
BiFunction<Apple, Apple, Integer> c3 =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
特殊的void兼容规则
如果一个Lambda的主体是一个语句表达式,它就和一个返回void的函数描述符兼容(当
然需要参数列表也兼容)。例如,以下两行都是合法的,尽管List的add方法返回了一个
boolean,而不是Consumer上下文(T -> void)所要求的void:
// Predicate返回了一个boolean
Predicate<String> p = s -> list.add(s);
// Consumer返回了一个void
Consumer<String> b = s -> list.add(s);
6.3 方法引用
在之前的表达写法中都是使用Lambda表达式来进行描述,使用方法引用可以使代码看上去更简洁
//使用Lambda方式
List<Apple> list1 = filterApples(apples, (Apple a) -> a.getWeight() > 150);
//使用方法引用
List<Apple> list = filterApples(apples, Apple::isGreenApple);
构建方法引用:
1、指向静态方法的引用 (Integer的parseInt方法,写作Integer::parseInt))
2、指 向 任意类型实例方法 的方法引用 (String 中的isEmpty() s::isEmpty() 实际就是指的的是一个对象的方法)
3、指向现有对象的实例方法的方法引用 (你在Lambda中调用一个已经存在的外部对象中的方法)
构造函数引用:
ClassName::new
适合Supplier的签名() -> Apple
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
等价于
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
7、Lambda表达式复合使用
1、比较符合器
逆序:
//根据苹果重量来进行排序
inventory.sort(comparing(Apple::getWeight).reversed());
比较器链
////根据苹果重量来进行排序,如果重量相同,在按照产地进行排序
inventory.sort(comparing(Apple::getWeight).reversed());
nventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getAddress));
2、谓语复合
谓词接口包括三个方法:negate、and和or
//negate 苹果不是红色的
Predicate<Apple> notRedApple = redApple.negate();
//and 查找重量比较重和颜色是红色的
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
//or 达要么是重(150克以上)的红苹果,要么是绿苹果
redApple.and(a -> a.getWeight() > 150) .or(a -> "green".equals(a.getColor()));
3、函数复合
Function接口为此配了andThen和compose两个默认方法
andThen: f(g(x))
compose: g(f(x))
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); //x=1 result = 4
Function<Integer, Integer> h = f.compose(g); // x=1 result = 3