Lambda表达式基础学习

一、Lambda表达式

1、Lambda的基本组成
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(); 
     }
 }

Lambda表达式基础学习

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