java8新特性 lambda表达式

      现在java已经发展到11了,如果你现在还没有了解lambda表达式的话,那么很幸运,你能看到我的这篇博客。写这篇博客,我由衷地希望能帮助到更多想要学习lambda表达式的人。我力图用最简单的语言,去讲清楚它。

我无不激动地向你介绍这个从java8就已经开始具有的新特性,java程序员新的宠儿,传说中的lambda表达式。它给编程提供了新的方式。虽然用面向对象的的编程都有替代的办法,但是,它实在是太好用了,所以从java8开始,lambda表达式已经被广泛支持了。

       那么就请一起来了解一下这个让人无比幸福的语法糖吧。

        什么是lambda表达式?它能做什么?如何使用它?

        从java诞生以来,面向对象的设计思路让人有一种非常亲切感觉,万物都是对象,我们所做的一切的工作,就是联系对象之间的关系,调用对象的方法,完成各类逻辑。文件是对象,连接是对象,就连class也是对象。面向对象的编程让我们遨游于对象的海洋。一切都非常美好。但是,在我们编程的过程中,我们逐渐发现,一个问题日益凸显而出,这个问题使得我们心里痒痒的,虽然那并不会影响我们的编程,但在编程过程中,它一直让我们觉得不舒服。那就是,我们一直很难把方法当作一个对象(虽然我们有反射,但是在平时,我们不轻易使用它)。如果能够把一个行为作为一个对象,那么在很多时候,把它作为参数传递给别的方法,可以极大的便利我们的编程。在这种背景诉求下,lambda表达式诞生了。

        要了解什么是lambda表达式,首先我们要了解什么是表达式。

比如 int a = 2; 

String str = "hello world";

以上这些就是我们不能再熟悉的表达式,它们做了这样一件事情:创建一个对象,然后声明了一个变量,然后对那个变量进行赋值。

那么,让我们再试着再大胆一点?

按照我们的希望,我们如果把方法作为对象会怎么样?

Function funtion = public static void hello(){
    System.out.println("hello world");
}

哇哦!这看起来太酷了。一个方法被赋值给一个变量!一个方法被赋值给了一个变量???

这是我们一直想要的,这实在是太美好了,这大概就是lambda表达式的雏形?

如果是的话,这让人既难过又开心。开心的是,我们居然能够把方法作为对象,这在以往的java程序里真的是不敢想象,天啊!这逆天的操作!而难过的是,这样写的话,未免也太麻烦了吧?简化!我们必须简化它!

想想看,public是声明调用范围的,public似乎可以去掉?就算要加也应该是加给function的。同样,static,似乎也可以去掉?它是来指明属于类的,还是对象的,就算要加,也应该加给function。

经过修改,现在lambda表达式变成了这样

Function function = void hello(){
    System.out.println("hello world");
}

这看起来实在不错。如果以后我们来使用的话,这样的确很简单。

但是能不能更简单?

可以。

真的吗?真的能再简化吗?

想想看,如果一个方法已经被赋值给一个变量了,那么就算这个方法没有方法名,也是无伤大雅的。好,去掉方法名。

而且现在的编译器已经足够聪明,它知道自己能返回什么,那么void也可以去掉。好,去掉void。

然后,我们在()和{}之间,我们加一个->来说明这是一个lambda表达式吧?

所以,最后,lambda表达式变成了这副样子!

Function function = ()->{System.out.println("hello world");};

这实在是让人感觉到无比惊喜,这将是一个伟大的发明。java函数式编程的时代似乎到来了!

但是,事实上,如果你把上面这段代码写在java程序里的话,是报错的= =

Function是什么?java8并没有它的存在。

如果,这能进行赋值的话,Function必须事先声明!

那么该如何声明这个Function呢?

首先,我会告诉你,Function的本质是接口(interface)。

所以,最后程序变成了这个样子。

public class Demo {
    public static void main(String[]args){
        Function function = ()->{System.out.println("hello world");} ;
    }
}
interface Function{
    void foo();
}

你一定会很惊讶。什么?Function是接口?你不是说它是方法吗?不是把方法赋值给function变量吗?Function怎么会是接口呢?

别急别急,你慢慢听我说。所以上述程序 可以视为为,把一个输出hello world的lambda表达式赋值给function变量。准确的说,是赋值给Function接口的变量。

那么,它该如何执行呢?其实,如果是以上程序的话,还不会有任何结果。你应该调用foo方法。如下:

public class Demo {
    public static void main(String[]args){
        Function function = ()->{System.out.println("hello world");} ;
        function.foo();
    }
}
interface Function{
    void foo();
}

没错,这样就可以执行,结果输出了hello world。

有人不禁问,这样到底有什么用。把一句话可以解决的问题硬生生写成5句话,这不是没事找事吗?

没错,在这种地方使用lambda表达式非但不简单,而是更加复杂!

但是,我要说的是,lambda表达式的出现,开启了java函数式编程的时代。而把lambda表达式等价于接口,更是一个高明之举!

为什么?因为,在没有lambda表达式之前。我们想要把一个方法传递给一个方法,往往是把含有该方法的接口传递给那个方法,然后在接口的方法中,完成行为的实现,即实现接口。

比如,下面这段代码你一定不陌生。

public class ThreadExample {
    public static void main(String[]args){
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(100);
            }
        });
        t.run();
    }
}

上面这段代码把Runable接口对象传递给Thread的构造方法作为参数,产生了一个线程类,最后运行了它。

把lambda表达式等价于接口,很大程度上,就是为了避免以上这种方法传递。

设想一下,要把方法A作为参数,传递给另一个方法B。要完成以下几步:第一,要先创建一个含有方法A的接口,第二,把创建好接口传递给那个方法B,第三,在接口中实现那个方法A。如此大费周章的去做一件吃力的事。

而lambda表达式如果可以直接等价于接口的话,那么可以极大的方便了这个操作。

(提示:因为lambda表达式是出于这个目的产生的,所以lambda表达式所对应的接口,只有一个方法!即单方法接口。

换言之,如果一个接口有两个方法,那么没有lambda表达式可以赋值给它。)

好,我们已经知道,lambda表达式的本质是只有一个方法的接口。而Runable接口完全满足这个条件。那么如果把代码转化成lambda表达式,就超级酷了!代码如下:

public class ThreadExample {
    public static void main(String[]args){
        Thread t = new Thread(()->{System.out.println(100);});
        t.run();
    }
}

令人窒息的操作!

我们把一个lambda表达式直接当作一个接口,直接传递进Thread的构造方法,相当于等价于Runable这个接口。

我,我已经激动地说不出话了!

有一天,我们学校要做学生信息系统,需要做3件事情。

1.能显示全部学生信息

2.显示带有条件的学生信息(比如姓氏为张的)

3.按照一定条件排序显示学生信息(比如按照年龄排序)

按照需求,我赶紧开始工作。为了简化问题,我假设学生只有序号,姓名,年龄,性别4个属性。

所以,我编写了一个学生类。

public class Student {
    private int id;
    private String name;
    private String sex;
    private int age;

    public int getAge() {
        return age;
    }

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


    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Student(int id, String name, String sex, int age) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}

这很容易,然后,我开始编写具体的主程序。

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class StudentExample {
    public static void main(String[]args){
        List<Student> list = Arrays.asList(
                new Student(1,"zhangsan","man",22),
                new Student(2,"liuqi","woman",21),
                new Student(3,"chenliu","woman",19),
                new Student(4,"wangwu","man",20),
                new Student(5,"lisi","man",19)
        );

        // show all students
        System.out.println("-----------------------------------");
        testStudentCondition(list, new Condition() {
            @Override
            public boolean test(Student student) {
                return true;
            }
        });
        System.out.println("-----------------------------------");
        // show zhang
        testStudentCondition(list, new Condition() {
            @Override
            public boolean test(Student student) {
                return student.getName().startsWith("zhang");
            }
        });


        System.out.println("-----------------------------------");
        // sort by age , and show all
        list.sort(new Comparator<Student>() {
            @Override
            public int compare(Student student, Student t1) {
                return student.getAge()-t1.getAge();
            }
        });

        testStudentCondition(list, new Condition() {
            @Override
            public boolean test(Student student) {
                return  true;
            }
        });

    }
    public static void testStudentCondition(List<Student>list,Condition condition){
        for(Student student:list){
            if(condition.test(student)){
                System.out.println(student);
            }
        }

    }
}
interface  Condition{
   boolean test(Student student);
}

我编写了一个testStudentCondition方法,利用condition对象,可以显示list中满足条件的学生。我对自己写的代码非常满意。

但是,我对这臃肿的接口传递非常不爽。

试试看lambda表达式?

我迫不及待的尝试用它改造我的代码,结果让我惊讶不已:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class StudentExample2 {
    public static void main(String[]args){
        List<Student> list = Arrays.asList(
                new Student(1,"zhangsan","man",22),
                new Student(2,"liuqi","woman",21),
                new Student(3,"chenliu","woman",19),
                new Student(4,"wangwu","man",20),
                new Student(5,"lisi","man",19)
        );

        // show all students
        System.out.println("-----------------------------------");
        testStudentCondition(list,(stu)->true);
        
        System.out.println("-----------------------------------");
        // show zhang
        testStudentCondition(list,(stu)->stu.getName().startsWith("zhang"));
        
        System.out.println("-----------------------------------");
        // sort by age , and show all
        list.sort((s1,s2)->s1.getAge()-s2.getAge());
        testStudentCondition(list,(stu)->true);
        
    }
    public static void testStudentCondition(List<Student>list,Condition condition){
        for(Student student:list){
            if(condition.test(student)){
                System.out.println(student);
            }
        }

    }
}

我实在没有想到,代码可以写的这样优雅,这种感觉真的是爽呆了!

有一天,我很无聊,想要循环输出学生信息,我用了for循环,还有增强for循环。

代码如下:

import java.util.Arrays;
import java.util.List;

public class StreamDemo { public static void main(String[]args){
        List<Student> list = Arrays.asList(
                new Student(1,"zhangsan","man",18),
                new Student(2,"liuqi","woman",19),
                new Student(3,"chenliu","woman",20),
                new Student(4,"wangwu","man",22),
                new Student(5,"lisi","man",21)
        );

        for(int i=0;i<list.size();i++){
                System.out.println(list.get(i));
        }
        for(Student s:list){
                System.out.println(s);
        }
    }
}

挺好!是的!

但是,我已经上瘾了,我想要知道,有没有更酷的写法。

比如lambda表达式?

你知道,我为什么喜欢它。

import java.util.Arrays;
import java.util.List;

public class StreamDemo { public static void main(String[]args){
        List<Student> list = Arrays.asList(
                new Student(1,"zhangsan","man",18),
                new Student(2,"liuqi","woman",19),
                new Student(3,"chenliu","woman",20),
                new Student(4,"wangwu","man",22),
                new Student(5,"lisi","man",21)
        );

        list.forEach((stu)->System.out.println(stu));
    }
}

天哪!这让人实在满足。

java8以后,list多了一种forEach遍历方式,需要传递的是一个接口,正合我意。

有人不禁会有这样的疑惑。

是不是,要使用lambda表达式,是不是要经常自己声明接口。

我要说的是,完全不用有这种担心。

java专门为来了lambda表达式准备了一个包(java.util.function) !

里面有一些常用的接口,这样我们不需要自己经常写接口,尽情狂欢吧?

比如,我们写的第一个lambda表达式可以不需要声明Condition接口了。

可以变成这样:

import java.util.function.Consumer;

public class Demo {
    public static void main(String[]args){
        Consumer co = (o)->{System.out.println("hello world");};
        co.accept(null);
    }

}

Consumer接口,接受一个参数,无返回类型。(值得一提的是,那个o,其实是Object o 忽略参数类型的简写)

lambda表达式的爽口,说几天也说不完,我也没有完全参透。

就先分享到这里。

请一起来享受java程序员的盛宴吧!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值