Java8实战之(二)Lamdba表达式

本章内容

  1. Lambda管中窥豹
  2. 在哪里以及如何使用Lambda
  3. 环绕执行模式
  4. 函数式接口,类型推断
  5. 方法引用
  6. Lambda复合

Lambda管中窥豹

可以把Lambda表达式理解为更简洁地表示可传递的匿名方法的一种方式,它没有名称,但是有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。下面我们来解释一下什么是匿名、函数、传递、简洁
匿名不像我们写的普通方法都有个明确的名称,写得少而想得多
函数我们说它是函数,是因为Lamdba函数不像方法那样属于特定的类。但是和方法一样,Lamdba有参数列表、函数主体、返回类型、还可能抛出异常
传递 Lambda表达式可以作为参数传递给方法或储存在变量中
简洁无需要匿名类那样写很多模板代码
在这里插入图片描述
在这里插入图片描述
由图3-1有三部分
(1)**参数列表:**这里它采用了Comparator中的compare方法的参数,两个Apple
(2)**箭头:**箭头把参数列表与Lamdba主体分割开。
(3)**Lamdba主体:**比较两个Apple的重量,表达式就是lamdba的返回值了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在哪里以及如何使用Lambda

在这里插入图片描述

函数式接口

在函数式接口上使用lamdba表达式,上述代码中,我们将lamdba表达式当做filter的第二个参数,因为它这里需要Predicate,而它恰恰是一个函数式接口,下面我们来解释什么是函数式接口。
从上一章中我们为fliter方法的行为参数化使用的是Predicate,他就是一个函数式接口,因为Predicate是一个接口并且只定义一个抽象方法,JavaApi中一些其他的函数式接口如下
在这里插入图片描述
在这里插入图片描述
Lamdba表达式允许你直接以内联的方式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体来说,是函数式接口一个具体实现的实例)。解决了以往匿名类带来的笨拙
在这里插入图片描述

函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描描述。例如,Runnable接口可以看作一个什么也不接受什么也不返回(void)的函数的签名,因为它只有一个叫作run的抽象方法,这个方法什么也不接受,什么也不返回(void)。
在这里插入图片描述
在这里插入图片描述

环绕执行模式

在这里插入图片描述

第一步:记得行为参数化

在这里插入图片描述

String result = processFile((BufferedReader br) -> 
 br.readLine() + br.readLine());

第二步:使用函数式接口来传递行为

在这里插入图片描述
在这里插入图片描述

第三步:执行一个行为

在这里插入图片描述

第四步:传递Lamdba

在这里插入图片描述

完整步骤

package com.learn.java8.second;

import java.io.BufferedReader;
import java.io.FileReader;

/**
 * 函数式接口
 */
@FunctionalInterface
public interface BufferedReaderProcessor {
    String process(BufferedReader bufferedReader) throws Exception;

}
class Test{

    // 现在你就可以把这个接口作为新的processFile方法的参数了:
    public static String processFile(BufferedReaderProcessor bufferedReaderProcessor){
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader("E:\\learn\\java8-master\\java8\\src\\main\\resources\\data.txt"));
            return bufferedReaderProcessor.process(bufferedReader);
        }catch (Exception ex){
            System.out.println("异常");
        }
        return null;
    }

    public static void main(String[] args) {
    // 传入Lamdba表达式
        String onLine = processFile((BufferedReader br)-> br.readLine());
        String twoLine = processFile((BufferedReader br)-> br.readLine() + br.readLine());
        System.out.println(onLine);
        System.out.println(twoLine);
    }
}

在这里插入图片描述

使用函数式接口

在这里插入图片描述
在这里插入图片描述

Predicate

在这里插入图片描述

Consumer

在这里插入图片描述
在这里插入图片描述

    public static <T> void forEach(List<T> list, Consumer<T> consumer){
        for (T item:list){
            consumer.accept(item);
        }
    }
        forEach(Arrays.asList(1,3,4,5,6),(Integer i) -> System.out.println(i));

Function

在这里插入图片描述
在这里插入图片描述


    /** 它接受一个泛型T的对象,并返回一个泛型R的对象。
     * @param list
     * @param consumer
     * @param <T>
     * @param <R>
     */
    public static <T,R> List map(List<T> list, Function<T,R> consumer){
        List<R> result = new ArrayList<>();
        for (T item:list){
            result.add(consumer.apply(item));
        }
        return result;
    }

        List<Integer> list = map(Arrays.asList("li","asasdasd","asdasdadasdasd"),(String s1) -> s1.length());
// [2, 8, 14]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

类型检查、类型推断以及限制

在这里插入图片描述

类型检查

Lamdba的类型是从使用Lamdba的上下文推断出来的,上下文(接收它传递的方法的参数,接受它的值的局部变量)中Lamdba表达式需要的类型称为目标类型,下面通过一个例子看看lamdba表达式的背后发生了什么。
在这里插入图片描述
在这里插入图片描述

同样的lamdba,不同的函数式接口

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.learn.java8.second;

public interface TestLadmdba {
    void test(Integer integer);

    public static void main(String[] args) {
        TestLadmdba i = (Integer p) -> test();
    }
    public static boolean test(){
        return false;
    }
}


在这里插入图片描述
在这里插入图片描述
你已经见过如何利用目标类型来೜查一个Lambda是否可以用于某个特定的上下文。其实,它也可以用来做一些略有不同的事:推断Lambda参数的类型。

类型推断

你还可以进一步简化你的代码.Java编译器会从上下文(目标类型)推断出用什么函数式接 口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通过目标类型来得到,这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可 以在Lambda语法中省去标注参数类型.换句话说,Java编译器会像下面这样推断Lambda的参数类型:
在这里插入图片描述
注意,当lamdba仅有一个类型需要推断的参数时,参数两边的括号也可以省略

使用局部变量

在这里插入图片描述
在这里插入图片描述
你可能会问自己,为什么ࡌ部变量有这些限制。第一,实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了
这个限制。
第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式(我们会在以后的各章中解释,这种模式会阻碍很容易做到的并行处理)
在这里插入图片描述
现在,我们来介绍你会在Java 8代码中看到的另一个功能:方法引用。可以把它们视为某些Lambda的快捷写法

方法引用

在这里插入图片描述

管中窥豹

在这里插入图片描述
如何构建方法引用
(1)指向静态方法的引用(如Integer的paraseInt方法),写成(Integer::paraseInt)
(2)指向任意类型实例方法的方法引用(例如String的length方法,写成String::length)
(3)指向现有对象的实例的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

构造函数引用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Lamdba和方法引用实战

在这里插入图片描述
在这里插入图片描述

第一步:传递代码

在这里插入图片描述

第二步:使用匿名类

在这里插入图片描述

第三步:使用Lamdba表达式

在这里插入图片描述

第四步:使用方法引用

在这里插入图片描述

复合Lamdba表达式的有用方法

在这里插入图片描述

比较器复合

在这里插入图片描述

  1. 逆序
    在这里插入图片描述

  2. 比较器链
    在这里插入图片描述

谓语副词

在这里插入图片描述

 Predicate<Apple> applePredicate = (Apple apple) -> apple.getColor().equals("red");
        Predicate<Apple> notRedApple = applePredicate.negate();

        Predicate<Apple> applePredicate = (Apple apple) -> apple.getColor().equals("red");
        Predicate<Apple> redAndApple = applePredicate.and((Apple apple) -> apple.getWeight() > 150);

函数复合

在这里插入图片描述

package com.learn.java8.second;

import java.util.function.Function;
import java.util.function.ToIntFunction;

public class Demo {
    public static void main(String[] args) {
        Function<Integer,Integer> function1 = (Integer x) -> x + 1;
        Function<Integer,Integer> function2 = (Integer x) -> x * 1;
        // andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数((10+1) * 1),是一个先前再后的思想
        Function<Integer,Integer> function3 = function1.andThen(function2);

        Integer result = function3.apply(10);
        System.out.println(result);

        // compose同样返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数((10*1)) + 1,是一个先后再前的思想
        Function<Integer,Integer> function4 = function1.compose(function2);
        System.out.println(function4.apply(10));

    }
}

在这里插入图片描述
文本转换
在这里插入图片描述

package com.learn.java8.second.demo;

import java.util.function.Function;

public class Letter {
    public static String addHeader(String text){
        return "Form zlx,zlx2 And Alan" + text;
    }

    public static String addFooter(String text){
        return text + "Kind regards";
    }

    public static String checkSpelling(String text){
        return text.replaceAll("lamdba","la");
    }

    public static void main(String[] args) {
        Function<String,String> addHeader = Letter::addHeader;
        Function<String,String> transformationPieline = addHeader.andThen(Letter::checkSpelling)
                .andThen(Letter::addFooter);
        String result = transformationPieline.apply("lamdba ladlads adasdads");
        System.out.println(result);
    }
}

数学中的类似思想

在这里插入图片描述

积分

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.learn.java8.second.demo;

import java.util.function.DoubleFunction;

public class Area {
    public double integrate(DoubleFunction<Double> function,double a,double b){
        return (function.apply(a) + function.apply(b)) * (b-a)/2.0;
    }

    public static void main(String[] args) {
        double result = new Area().integrate((double x) -> x + 10,3,7);
        System.out.println(result);
    }
}

总结

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值