JDK8新特性(一):Lambda表达式

1.首先来个Demo

public class LambdaDemo {
    public static void main(String[] args) {
        //开启一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程执行代码");
            }
        }).start();
    }
}

分析:

   当我们以 new Thread().start() 的方式启动一个线程时,我们做了如下3件事情:

  1. 定义了一个没有名字的类(匿名类)
  2. 这个类的参数参数,调用的是 Runnable 接口
  3. 我们通过 new Runnable(){...}的方式创建了这个类,并重写了该接口的 run() 方法

   在这个示例中,其实我们关注的并不是 new Runnable() 这个过程,而是如下2点:

  1. run() 方法    (参数情况)
  2. {...}方法体中执行的代码

Lambda表达式的出现

    在以上Demo中,针对使用匿名内部类语法冗余的问题,JDK8推出了 Lambda 表达式。

  1. Lambda表达式体现的是函数式编程思想,只需要将要执行的代码放到函数中即可(函数就是类中的方法);
  2. Lambda表达式就是一个匿名函数,我们只需要将执行的代码放到 Lambda 表达式中即可。

Lambda表达式格式

    Lambda省去面向对象的条条框框,Lambda的标准格式由3部分组成:

(参数类型 参数名称) -> {
    方法体;
    return 返回值;
}

  格式说明: 

  1. (参数类型 参数名称):参数列表部分
  2. {...}:方法体,即要执行的代码部分
  3. ->:箭头,无实际含义,起到连接参数列表方法体的左永刚

Lambda 表达式的省略规则

  1. 小括号中的参数类型可以省略。
  2. 如果小括号中只有一个参数,那么可以省略小括号。
  3. 如果大括号中只有一条语句,那么可以同时省略大括号、return关键字及语句分号。

使用Lambda表达式改造

public class LambdaDemo {
    public static void main(String[] args) {
        //匿名内部类方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程执行代码了");
            }
        }).start();

        //体验Lambda表达式
        new Thread(() ->{
            System.out.println("Lambda表达式执行了");
        }).start();
    }
}

Lambda表达式的好处

☆ 可以简化匿名内部类,让代码更加精简

☆ Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

Lambda表达式练习

1.无参数无返回值的Lambda表达式

   ①定义一个接口类ISwim,接口中定义一个抽象方法 swimming();

/**
 * ISwim接口
 */
public interface ISwim {
    //swimming()抽象方法
    public abstract void swimming();
}

   ②定义一个方法 goSwimming(ISwim swim),注意该方法参数是一个接口类ISwim。然后 main 中调用该方法;

public class LambdaDemo {
    public static void main(String[] args) {
        //1.使用匿名内部类方式
        goSwimming(new ISwim() {
            @Override
            public void swimming() {
                System.out.println("我是匿名内部类的游泳");
            }
        });

        //2.Lambda表达式方式
        goSwimming(()->{
            System.out.println("我是Lambda表达式的游泳");
        });

    }

    //goSwimming(ISwim swim) 方法
    public static void goSwimming(ISwim swim){
        swim.swimming();
    }
}

//测试结果:
//    我是匿名内部类的游泳
//    我是Lambda表达式的游泳

2.有参数有返回值的Lambda表达式

   ①定义一个接口类ISmoke,接口中定义一个具有返回值(int)的抽象方法 smoking();

/**
 * ISmoke接口
 */
public interface ISmoke {
    //smoking()抽象方法
    public abstract int smoking(String name);
}

   ②定义一个方法 goSwimming(ISwim swim),注意该方法参数是一个接口类ISwim。然后 main 中调用该方法;

public class LambdaDemo {
    public static void main(String[] args) {
        //1.使用匿名内部类方式
        goSmoking(new ISmoke() {
            @Override
            public int smoking(String name) {
                System.out.println("匿名内部类:抽了"+ name + "牌香烟");
                return 5;
            }
        });

        //2.使用Lambda表达式方式
        goSmoking((String name)->{
            System.out.println("Lambda表达式:抽了"+ name + "牌香烟");
            return 6;
        });
    }

    //有参数有返回值的Lambda
    public static void goSmoking(ISmoke smoke){
        int i = smoke.smoking("中华");
        System.out.println("返回值:"+i);
    }
}

//测试结果:
//    匿名内部类:抽了中华牌香烟
//    返回值:5
//    Lambda表达式:抽了中华牌香烟
//    返回值:6

日常工作中的案例

       我们有一个List集合,集合中存放了 4个Person对象,Person对象中有 name,age,height 属性。对集合进行排序,我们会使用到 Collections 工具类中的 Collections.sort()方法。

      sort()方法中传递的两个参数:1.list集合   2.Comparator 比较器。我们发现 Comparator 是一个接口,所以此处也可以使用 Lambda 表达式来替代匿名内部类。

/**
 * Person实体类
 */
public class Person {

    private String name;

    private int age;

    private int height;

    public Person(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}
public class CollectionsLambdaDemo{

    public static void main(String[] args) {
        List persons = new ArrayList<>();
        persons.add(new Person("刘德华",58,174));
        persons.add(new Person("张学友",56,176));
        persons.add(new Person("郭富城",54,171));
        persons.add(new Person("黎明",53,178));

        //1.匿名内部类,对集合进行排序
        Collections.sort(persons, new Comparator() {
            //年龄降序排序
            @Override
            public int compare(Person o1, Person o2) {
                return o2.getAge() - o1.getAge();
            }
        });

        for (Person person: persons) {
            System.out.println(person);
        }

        System.out.println("-----------------------------------");

        //2.Lambda表达式
        Collections.sort(persons,(Person o1,Person o2) ->{
            return o1.getAge() - o2.getAge();
        });

         for (Person person: persons) {
             System.out.println(person);
         }
    }
}

//测试结果:
    Person{name='刘德华', age=58, height=174}
    Person{name='张学友', age=56, height=176}
    Person{name='郭富城', age=54, height=171}
    Person{name='黎明', age=53, height=178}
    -----------------------------------
    Person{name='黎明', age=53, height=178}
    Person{name='郭富城', age=54, height=171}
    Person{name='张学友', age=56, height=176}
    Person{name='刘德华', age=58, height=174}

Lambda表达式使用的前提条件

1.方法的参数局部变量类型必须为接口,才能使用Lambda

   eg:局部变量类型:(Runnable是一个接口)
       匿名内部类方式:
             Runnable r = new Runnable(){
                 @override
                 public void run(){
                     System.out.println("xxx");
                 }
             }
       这种情况,你要来个局部变量,可以使用 Lambda表达式
            Runnable r = ()-> System.out.println("xxx");

2.接口中有且仅有一个抽象方法

       JDK8中,只有一个抽象方法的接口称为函数式接口,我们就能使用 Lambda。
       针对一个接口中,有两个抽象方法:JDK8为我们新增了一个注解:@FunctionalInterface。它能够帮助我们检测这个接口是不是只有一个抽象方法,如果有两个抽象方法,则会报错。

Lambda表达式原理分析

       请跳转学习:Lambda表达式原理分析

Lambda 和 匿名内部类对比

1.所需的类型不一样

     匿名内部类,需要的类型可以使类,抽象类,接口;

     Lambda表达式,需要的类型必须是接口。

2.抽象方法的数量不一样

     匿名内部类所需的接口中抽象方法的数量随意;

     Lambda表达式所需的接口只能有一个抽象方法。

3.实现原理不同

     匿名内部类是在编译后,会形成额外的一个 类名$0 的.class文件

     Lambda 表达式实在程序运行的时候动态生成 .class 文件

总结

       当接口中只有一个抽象方法时,建议使用 Lambda 表达式;其他情况下,还是需要使用匿名内部类。


附:JDK8新特性(目录)

       本目录为 JDK8新特性 学习目录,包含JDK8 新增全部特性的介绍。

       如需了解,请跳转链接查看:我是跳转链接


博主写作不易,来个关注呗

求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙

博主不能保证写的所有知识点都正确,但是能保证纯手敲,错误也请指出,望轻喷 Thanks♪(・ω・)ノ

你可能感兴趣的:(JDK8新特性)