Lambda

1、简单介绍

Lambda表达式是JDK8的新特性,作用就是简化我们的代码书写。

用demo来进行说明使用lambda表达式的优势:

public class DemoOne {
    public static void main(String[] args) {
        // 一定要注意:这里是start方法调用了run方法。注意这里的执行顺序
        new Thread(new Runnable1()).start(); // 这行代码就相当于是下面的代码,但是这么写的话,还需要写一个类来进行实现,然后new一个对象传入进来。使用下面方式简化

        // 首先分析一波:这里是接口,那么利用的肯定是多态机制,传入的最终是对象形式。但是只是使用一次,所以这里利用的是匿名对象
        // 对象得去实现接口中的抽象方法,方便进行调用。这里匿名内部类直接进行实现,new了一个匿名对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开启了一条新线程1");
            }
        }).start();

        // 上面的代码在jdk8中又进行优化,可以写成下面这种方式来进行简化。
        // 函数式编程: 强调: 做什么
        // 只用了一行代码写了上面五行代码做的事情。
        // 实行start方法,自动调用了里面的run方法
        new Thread(() ->  System.out.println("开启了一条新线程2") ).start();
        new Thread(() -> System.out.println("开启了一条新线程3")).start();
        new Thread(() -> System.out.println("开启了一条新线程4")).start();
        new Thread(() -> System.out.println("开启了一条新线程5")).start();
        new Thread(() -> System.out.println("开启了一条新线程6")).start();
        new Thread(() -> System.out.println("开启了一条新线程7")).start();

    }

   static class Runnable1 implements Runnable{
        @Override
        public void run() {
            System.out.println("开启了一条新线程0");
        }
    }
}

通过上面的一个demo,可以直接明白使用lambda表达式可以简化我们的代码。将原来的匿名内部类写成了简化方式了,节省了代码,这是最直观的感觉。看下Runnable接口源码:

@FunctionalInterface 
public interface Runnable {
    public abstract void run();
}

一个函数式注解,接口中只有一个无参无返回值的方法。对应着我们上面写的代码

new Thread(() -> System.out.println("开启了一条新线程")).start();

这里的():代表的是run方法参数的位置,是无参的;

System.out.println("开启了一条新线程"):对应着的是函数体中的内容,没有返回值。

所以这里先小结一下:

1、使用lambda表达式是为了简化代码书写的;
2、只要是能够使用匿名内部类来完成的,那么一定可以使用lambda表达式来进行替代。

2、使用Lambda前提

举更多的demo来进行列举lambda使用的好处的地方。在举例子之前,首先说明下,使用lambda表达式的前提:

1、函数式接口:接口中只有一个方法(Object类中的方法和默认方法排除)。在使用的时候可以看到有@FunctionalInterface修饰的接口,也    可以没有这个注解来进行说明。
2、使用的场景:一般作为方法的参数(使用多),另外一种情况是作为返回值的方式来进行使用(使用少)。

3、例子说明:

基于上面的前提,接口一般都是定义好的。所以我们更关注的是第二种方式下的使用:

3.1、无参无返回值

上面的demo引入的就是一个典型的例子,这里不在举例子来进行说明:

public class DemoOne {
    public static void main(String[] args) {
        // 一定要注意:这里是start方法调用了run方法。注意这里的执行顺序
        new Thread(new Runnable1()).start(); // 这行代码就相当于是下面的代码,但是这么写的话,还需要写一个类来进行实现,然后new一个对象传入进来。使用下面方式简化

        // 首先分析一波:这里是接口,那么利用的肯定是多态机制,传入的最终是对象形式。但是只是使用一次,所以这里利用的是匿名对象
        // 对象得去实现接口中的抽象方法,方便进行调用。这里匿名内部类直接进行实现,new了一个匿名对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开启了一条新线程1");
            }
        }).start();

        // 上面的代码在jdk8中又进行优化,可以写成下面这种方式来进行简化。
        // 函数式编程: 强调: 做什么
        // 只用了一行代码写了上面五行代码做的事情。
        // 实行start方法,自动调用了里面的run方法
        new Thread(() ->  System.out.println("开启了一条新线程2") ).start();
        new Thread(() -> System.out.println("开启了一条新线程3")).start();
        new Thread(() -> System.out.println("开启了一条新线程4")).start();
        new Thread(() -> System.out.println("开启了一条新线程5")).start();
        new Thread(() -> System.out.println("开启了一条新线程6")).start();
        new Thread(() -> System.out.println("开启了一条新线程7")).start();

    }

   static class Runnable1 implements Runnable{
        @Override
        public void run() {
            System.out.println("开启了一条新线程0");
        }
    }
}

3.2、无参有返回值

// 在这个类中来测试无参无返回值
public class Demo1 {
    public static void main(String[] args) {
        // 调用invoke方法,最终是要调用里面的show方法。看下show方法是一个有参无返回值的
        // invoke方法调用了里面的show方法。show方法需要一个参数,这里传入33。lamba表达式表示了show方法的具体执行方式
        // 这里的lambda表达式不是代表MyInterface myInterface,而是里面的方法的具体执行方式。【重点】
         invoke(33,(n)->System.out.println("有参无返回值"));
    }

    // 要进行调用,因为这里接口中的方法需要进行调用,所以需要一个参数,在这里进行调用
    public static void invoke(Integer age,MyInterface myInterface) {
        myInterface.show(age);
    }
}

 // 这里加不加@FunctionalInterface都可以
 interface MyInterface {
    void show(Integer integer);
}

3.3、有参无返回值

public class Demo2 {
    public static void main(String[] args) {
        // print方法来进行调用,一看参数是一个接口,应该是匿名对象调用了fun方法,print方法在进行执行的时候,传入了匿名对象,然后匿名对象调用了fun方法
        // fun方法如何进行实现?lambda来进行实现的。将其进行还原
        /**
         * new MyInterface(){
         *     @Override
         *     public String fun() {
         *         System.out.println("开启了一条新线程0");
         *      }
         * }
         */
        // 有一个返回值,需要来进行接收。
        print(()->"hello,无参有返回值");
    }

    public static void print(MyInterface lambda) {
        // lambda来实现里面具体的逻辑,节省代码
        String s = lambda.fun();
        System.out.println(s);
    }

    static interface MyInterface {
        String fun();
    }
}

3.4、有参有有返回值

public class Demo3 {
    public static void main(String[] args) {
        // 调用的时候,传递参数,然后匿名对象调用show方法,lambda中有具体的实现方式。
        fun("hello,有参有返回值",(n)->n);
    }

    public static String fun(String str,MyInterface myInterface){
        String show = myInterface.show(str);
        System.out.println(show);
        return show;
    }


    static interface MyInterface{
        String show(String s);
    }
}

4、总结

1、使用lambda表达式的前提:不论接口上是否有函数式接口注解,只看里面的方法(排除默认方法和Object中的方法);
2、本质:利用匿名内部类的方式,节省了代码。在lambda中的方法参数中没有写参数的具体参数的数据类型是因为会自动推理。
   只不过省略了匿名对象对具体方法的实现,而是在lambda中进行具体实现。
3、无关紧要的:lamba中只有一个参数,可以不写括号;lambda中参数中可以不写具体的参数类型。函数体中只有一行代码,可以省略大括号;
    如果函数体中只有一行代码,而且是返回值,那么return也可以不写。