天天看点

Lambda表达式个人理解一、学习背景二、简述三、语法四、结合方法引用 

一、学习背景

最近看了许多较新的教学视频,其中的讲师们在许多代码中使用了Lambda表达式,初学者看来觉得非常难以理解。这导致整个教学视频无法继续看下去。我在当时也是这个感觉,所以去简单的学习了一下Lambda表达式,在这里说一下自己对于Lambda表达式的理解。

二、简述

Runnable runnable = () -> {
    System.out.println("test");
};
runnable.run();
           

这是一段非常简单的Lambda表达式。如果理解起来比较困难的话,他可以完全的转化为如下代码。

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("test");
    }
};
runnable.run();
           

学过Java并发的朋友们都知道实现Runnable接口是实现多线程的一个常用的方法。

但是我们现在用到的不是他的这个特性。

我们知道,接口是不能够被直接实例化的。所以我们如果想对一个接口进行实例化的时候,必须要给他一个实现类。

那么我们转化后的代码可以看出,我们在 “ = ” 的后面实现实现了一个匿名实现类,用来实现Runnable接口的类。

然后将这个实现类赋给了runnable对象。

让我们回到上面的代码。我们可以看出Lambda所做的事情,和转化后的代码所做的事是一样的。

所以个人理解:Lambda表达式可以用来为接口简便的生成一个实现类。

在Lambda表达式中我们可以看出方法体就是代表着Runnable接口中run方法的方法体。

那么这个时候包括我自己刚刚学习的时候也会有一个疑问,凭什么它就代表run的方法体呢?如果有其他的方法呢?

这个时候Lambda表达式的一个特性,或者说是要求完美的解决了这个问题:

Lambda表达式只能用来实例化单抽象方法的接口 ---> “函数式接口”。

这样问题就解决了,我的接口里只有一个方法,那么当然,方法体就是写给他的啊。

那么接下来我简单的说一下Lambda表达式的语法。

三、语法

Lambda表达式一般形式如下:

Lambda表达式个人理解一、学习背景二、简述三、语法四、结合方法引用 
Runnable runnable = () -> {
    System.out.println("test");
};
runnable.run();
           

如同上方的写法,我们知道run方法是无参的,所以括号里只需要置空即可。

Consumer consumer = (x) -> {};

//只有一个参数,可以省略参数的括号
Consumer consumer1 = x -> {};

//两个参数
BiConsumer biConsumer = (x, y) -> {};
           

 JDK1.8之后为了我们使用Lambda表达式方便,官方增加了许多这样的单方法接口。

上方代码的两个类都是单方法接口,只不过一个是接单参,一个是接双参

但是这里有一点:如果方法仅有一个参数的时候,括号可以被省略。

另外,如上图的x,y,参数类型是可以被省略的。

如果方法体只有一行语句,并且此语句为返回值时,方法体的大括号可以被省略,例子如下:

Function function = (x) -> {
    return "abc";
};

//只有一行一句,并且其为返回值,方法体大括号可省略
Function function1 = (x) -> "abc";
           

四、结合方法引用 

方法引用也是JDK1.8中新特性之一。他用方法的名字来指向一个语法。正好可以与Lambda表达式一起合作编写出非常简洁的代码(但真的很难理解)。

对于方法的引用。我个人理解是:这个方法体仅仅用来调用另一个方法。

方法的引用大致分为如下几类:(这些类型的前一个代码均是未优化的,后一个代码均是优化后的)

1、静态方法的引用

public class Example {

    public static void main(String[] args) {
        //常规写法
        Consumer<String> consumer = x -> {
            Example.say(x);
        };
        consumer.accept("abc");
    }

    private static void say(String o) {
        System.out.println(o);
    }

}
           
public class Example {
    
    public static void main(String[] args) {
        //在上方代码中,我们的Lambda表达式的内容仅仅是用来调用Example的say静态方法
        //另外我们Lambda的参数正好是say所需要的参数
        //那么我们就可以改成如下这样
        Consumer<String> consumer = Example::say;
        consumer.accept("abc");
    }

    private static void say(String o) {
        System.out.println(o);
    }

}
           

2、实例方法的引用

public class Example {

    public static void main(String[] args) {
        Example e = new Example();
        //未经过转变的代码
        Consumer<String> consumer = x -> {
            e.say(x);
        };
        consumer.accept("abc");
    }

    private void say(String o) {
        System.out.println(o);
    }

}
           
public class Example {

    public static void main(String[] args) {
        Example e = new Example();
        //我们仅仅调用了e实例的say方法,并且lambda接受的参数与方法相同
        //可以转化为如下代码
        Consumer<String> consumer = e::say;
        consumer.accept("abc");
    }

    private void say(String o) {
        System.out.println(o);
    }

}
           

3、对象方法的引用

public class Example {

    public static void main(String[] args) {
        BiConsumer<Example, String> consumer = (Example e, String x) -> {
            e.say(x);
        };
        consumer.accept(new Example(), "abc");
    }

    private void say(String o) {
        System.out.println(o);
    }

}
           
public class Example {

    public static void main(String[] args) {
        //当我们对Lambda表达式传递多个参数的时候
        //我们如果想通过Lambda表达式第二个及以后的参数作为
        //Lambda表达式第一个参数所持有的方法的参数的话,可以作出如下变化。
        BiConsumer<Example, String> consumer = Example::say;
        consumer.accept(new Example(), "abc");
    }

    private void say(String o) {
        System.out.println(o);
    }

}
           

4、构造方法的引用

public class Example {

    private String content;
    
    public static void main(String[] args) {
        //未经过转化的代码
        Consumer<String> consumer = (x) -> {
            new Example(x);
        };
    }

    Example(String content){
        this.content = content;
    }

}
           
public class Example {

    private String content;

    public static void main(String[] args) {
        //如果这个方法仅仅是用来调用一个构造函数的话,并且lambda表达式的参数与构造方法相同
        //则可以转化为如下代码
        Consumer<String> consumer = Example::new;
    }

    Example(String content){
        this.content = content;
    }

}
           

从中我们可以看出,使用方法引用的最关键的地方就是调用的方法参数与我们Lambda表达式的参数相同。

只有这样我们才可以将Lambda表达式与方法饮用结合在一起,编写出更简洁的代码(当然也更难以理解)。

这是我对Lambda表达式的个人理解,如果有错误请务必指正,感谢阅读。