Lambda表达式详解(java8新特性,函数式编程)

在JDK1.8中引入了一个重要的新特性:Lambda表达式。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑,在学习Lambda表达式前我们先来思考一下为什么会出现它。

1.为什么会出现Lambda表达式

首先我们思考下在java中如何表示一个值?很简单,定义一个变量,然后赋值
在这里插入图片描述

那么如何表示一段代码块的执行逻辑呢?也很简单,定义一个函数,函数方法体写上对应的执行逻辑
在这里插入图片描述

可如果我想把这个函数赋予某个变量(aBlockOfCode)并当做入参传递呢?

比如说如此:

在这里插入图片描述

在Java 8之前,这么做是不合法的。但是Java 8问世之后,利用Lambda特性,就可以做到了,并且Lambda的设计者还很贴心的帮我们简化了这一定义:

在这里插入图片描述

可以看到,我们非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式

而这个变量的类型是什么呢?

Java8中,定义了一种新的接口类型: 函数式接口,一个接口函数需要被实现的接口类型,我们叫它”函数式接口“ ,并且还为这个接口定义了一个专属声明 @FunctionalInterface ,在接口上加上该声明代表该接口内只有一个函数需要被实现

在这里插入图片描述

其实我们发现,上述表达式其实通过定义一个借口的实现类,然后实例化也能达到同样的目的,但是代码量上却多了很多。

在这里插入图片描述

这也是Lambda表达式的最直观的作用: 使得代码变得异常简洁。

而且Lambda结合Java8的众多新特性:FunctionalInterface Lib, forEach, stream(),method reference等你会看到这一优势会被无限放大!

2.Lambda表达式的基础语法

Lambda 表达式的2种语法格式如下:

  1. (parameters) -> expression
  2. (parameters) -> {statements;}

以下是lambda表达式的重要特征:

1.可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

2.可选的参数圆括号:一个参数无需定义圆括号,但0或多个参数需要定义圆括号。

3.可选的大括号: 如果表达式只有一行,那么表达式两边的花括号可以省略 。

4.可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda 表达式的简单例子

  • 1.多参数的情况

    (1). lambda表达式的基本格式为(x1,x2)->{表达式…};

    (2).在上式中,lambda表达式带有两个参数,此时参数类型可以省略,但两边的括号不能省

    (3)…如果表达式只有一行,那么表达式两边的花括号可以省略

    //接受2个带参参数,并返回他们的差值 
    (int x, String y) -> String.valueOf(x) + y  
    
    // 4. 接收2无参参数,返回他们的和  
    (x, y) -> x – y  
    
    
  • 2.无参数的情况

    参数的括号不能省略, 其他语法同多参数

//创建并启动一个线程
new Thread(()-> System.out.println("hello, i am thread!")).start();
  • 3.一个参数的情况

    可以省略参数的括号和类型, 其他语法同多参数

//  接收一个参数,可以省略参数的括号和类型
x -> 2 * x  
 
// 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

3.Java8中提供的常用函数式接口

前面我们说了,为了配合Lambda表达式的使用,Java8中定义了一种新的接口类型: 函数式接口,即一个接口函数需要被实现的接口类型,由 @FunctionalInterface 声明。其实Java为了方便我们的使用,还提供了很多常用函数式接口。

利用函数式接口包:java.util.function

在这里插入图片描述

常见的有
1.消费型接口 Consumer : void accept(T t):接收一个参数进行消费,但无需返回结果。

2.供给型接口 Supplier : T get():返回一个自定义数据

3. 函数型接口 Function<T,R> : R apply(T t):传入一个参数,返回想要的结果。

4. 断言型接口 Predicate : boolean test(T t):传入一个参数,返回一个布尔值

5. 其他类似接口
UnaryOperator:一元操作符,Function的子类,只是该接口的输入参数和返回结果必须是同一类型。

BiFunction<T, U, R>:比Function高级一点,可以接收两个参数,应用后也还是返回一个结果。

BiConsumer<T,U>:同样比Consumer高级一点,可以接收两个参数进行消费而不需返回结果。

BiPredicate<T,U>:同样比Predicate高级一点,可以接收两个参数,判断后也还是返回一个bool值。

举例:
在这里插入图片描述

4.方法引用

Java 8 中还新增了一个关键字“ :: ”我们可以通过 " :: " 关键字来访问类的构造方法,对象方法,静态方法

演示如下:

1.先自定义一个类

class Demo {
    
    
    //无参构造
    Demo() {
    
    }
    //有参构造
    Demo(String something) {
    
    
        System.out.println(something);
    }
    //多参构造
    Demo(String something,Integer num) {
    
    
        System.out.println(something+num);
    }

    //静态方法
    static String startsMethod(String s) {
    
    
        return String.valueOf(s.charAt(0));
    }

    //
    String methodOne(String s) {
    
    
        return String.valueOf(s.charAt(s.length()-1));
    }

    void methodTwo() {
    
    
        System.out.println();
    }
}

2.先定义一个函数式接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tEDdjg5i-1606478057603)(Lambda表达式详解,java8新特性.assets/1606463685803.png)]

接口 IConvert, 传参为类型 F,返回类型 T。

接下来让我们演示通过 ::关键字来进行方法访问。

  1. 访问静态方法

     IConvert<String,String> ic1 =  Demo::startsMethod;
    ic1.convert("静态方法访问");
    
  2. 访问对象方法

     IConvert<String,String> ic2 =  new Demo()::methodOne;
     ic2.convert("对象方法访问");
    
  3. 访问构造方法

    IConvert<String,Demo> ic3 =  Demo::new;
    ic3.convert("构造方法访问");
    

我们可以这样认为,将接口IConvert<F,T>当做一个定义好的模板, Demo类中的各个方法当做这个模板的实现,只要Demo中的方法符合 IConvert接口的入参与出参定义,就可以直接将方法赋予模板。

引用于参考:
https://www.zhihu.com/question/20125256/answer/324121308

猜你喜欢

转载自blog.csdn.net/weixin_43828467/article/details/110244897