• Java 中的 Lambda 表达式


    Java 中的 Lambda 表达式

    本文连接:https://code.csdn.net/liuche911/lambdainjava/file/LambdainJava.md

    本文翻译自Oracle官方Java教程 "The Java(TM) Tutorials" 中的Lambda表达式一节。我从新写了代码,所以可能跟原文不大一样。

    另外就是我没有搜索是否有人已经翻译过这段教程,同时我相信网上关于Lambda表达式的帖子多如牛毛。。。本文作为自我总结的意义显然高于传播技术 :)

    先看一个例子

    假设我们有一个 Person 类,大致如下:

    ```Java
    public class Person {
        private String name;
        private int age;
    
        public static enum Sex {
            MALE, FEMALE
        };  
        private Sex sex;
        private double height;
        private double weight;
    
        public Person(String name, int age, Sex sex, double height, double weight) {
            super();
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.height = height;
            this.weight = weight;
        }
    
        @Override
        public String toString() {
            return String.format("%s Age:%d, Sex:%s%n", name, age,
                    sex == Sex.MALE ? "male" : "female");
        }
    
        //省略了set&get方法
    
    }
    ```
    

    再来一个 PersonList 类存放一组人,大致如下

    ```Java
    public class PersonList {
    
        private List<Person> persons;
    
        public PersonList(List<Person> persons) {
            super();
            this.persons = persons;
        }
    
        public boolean addPerson(Person person) {
            return this.persons.add(person);
        }
    
        public boolean removePerson(Person person) {
            return this.persons.remove(person);
        }
    }
    ```
    

    在给个简单的测试函数吧,假设就在 main 函数里实现:

    ```Java
        public static void main(String[] args) {
            PersonList personList = new PersonList(new ArrayList<Person>());
            personList.addPerson(new Person("老王", 88, Person.Sex.MALE, 1.7, 60));
            personList.addPerson(new Person("小丽", 12, Person.Sex.FEMALE, 1.60, 40));
            personList.addPerson(new Person("小白", 19, Person.Sex.MALE, 1.78, 66));
            personList.addPerson(new Person("小美", 22, Person.Sex.FEMALE, 1.7, 50));
        }
    ```
    

    现在我们需要编写一个函数,找出这一组人里,年纪小于等于(比方说)50岁的人,该怎么办呢?

    普通青年的做法

    ```Java
        /**
         * 普通青年 返回年纪小于age的人
         * 
         * @param age
         *            小于该年龄
         */
        public void printPersonAgeYoungerThan(int age) {
            for (Person p : persons) {
                if (p.getAge() <= age) {
                    System.out.println(p.toString());
                }
            }
        }
    ```
    

    这样做固然是满足需求,而且还没有“二逼”到 if (p.getAge() <= 50) 的地步,些许还有点灵活性;但是如果用户还想查询年纪大于50的人呢?再写一个函数?那么查询年纪大于18又小于50的人呢?且看文艺青年是怎么做的:

    文艺青年的做法

    ```Java
        /**
         * 文艺青年 返回年纪大于等于low,小于等于high的人
         * 
         * @param low
         * @param high
         */
        public void printPersonAgeIn(int low, int high) {
            for (Person p : persons) {
                if (p.getAge() >= low && p.getAge() <= high) {
                    System.out.println(p.toString());
                }
            }
        }
    ```
    

    这下似乎好多了,“小于等于50岁”可以理解为“大于等于-100岁,小于等于50岁”。大于多少岁的时候,上界即使不放心,那就飚到10000岁!再不放心就 Integer.MAX_VALUE ,内心终于得到平静……

    不过呀,用户可能还没完,他说不定想查询“满足美国服义务兵役条件的人~”,即“年龄在18到25岁的男性”。嘿嘿,烦了吧,还好我们有更厉害的程序猿青年!

    程序猿青年的做法

    ```Java
        public static interface CheckPersons {
            boolean test(Person p);
        }
    
        /**
         * 程序猿:返回满足CheckPersons tester的test方法的人
         * 
         * @param tester
         */
        public void printPersonAccordinTester(CheckPersons tester) {
            for (Person p : persons) {
                if (tester.test(p)) {
                    System.out.println(p.toString());
                }
            }
        }
    ```
    

    你再看~作为API提供者,我不干啦,接口甩出来,调用放好,从此放权给用户,想查询什么条件,自己写吧,我都能满足你。那么作为客户,可以考虑使用静态类实现CheckPerson接口方便经常调用,也可以图方便使用匿名类:

    ```Java
            personList.printPersonAccordinTester(new PersonList.CheckPersons() {
    
                @Override
                public boolean test(Person p) {
                    return p.getSex() == Person.Sex.MALE && p.getAge() >= 18
                            && p.getAge() <= 25;
                }
    
            });
    ```
    

    Wo!看来我们找到了问题的通解,但是,我们还没有提到Lambda表达式吧?这不是本文的重点么?

    在Eclipse中使用Lambda表达式

    首先要声明的是Lambda表达式是Java8的新特性之一,如果你发现下文的代码无法编译,其实只是因为你没有安装最新的JDK罢了,下载连接

    另外你还需要使Eclipse支持Java8,只需这样:官方教程

    这样做之后,可能因为JRE为设为jre8所以无法编译,见下图:

    eclipse build path

    如果原来使用的是jre7的话,需要选择 Edit ,选择Alternate JRE:,再选择 Installed JREs... ,再选择jre8的安装目录就可以自动识别出来。

    使用Lambda表达式

    Lambda表达式最适合的地方大概就是实现只有一个抽象函数的接口。且看:

    ```Java
            personList.printPersonAccordinTester(p -> p.getSex()==Person.Sex.MALE && 
                    p.getAge()>=18 && p.getAge()<=25 );
    ```
    

    是不是很清晰? Lambda表达式的语法很简单变量声明(忽略类型)+表达式。当变量多于一个时,用 (v1,v2)表示,表达式多于一行时用{e1; e2; e3; return e4;}表示。

    需要注意的是,当使用大括号后,括号内的全部内容就相当于替代了接口函数的全部内容,所以必要的 return 就不能省略了。

    一个小优化

    像我们的例子里定义的这个接口——输入参数,返回boolean——其实非常常用,Java在java.util.function中定义了一簇类似的函数,它们使用了泛型,足够我们使用了,这样就不必自己定义接口了。

    注意这依然是Java8的新特性

    于是乎 printPersonAccordinTester() 可以改写成这样: Java public void printPersonAccordinTester(Predicate<Person> tester) { for (Person p : persons) { if (tester.test(p)) { System.out.println(p.toString()); } } }

    全部的java.util.function接口

    让Lambda无处不在

    让我们把 print方法更加定制化一些,不光能自定义选择,还可以自定义查询的内容,以及查询后做什么:

    ```Java
        public void dealTheList(Predicate<Person> tester,
                Function<Person, String> mapper, Consumer<String> dealer) {
            for (Person p : persons) {
                if (tester.test(p)) {
                    String s = mapper.apply(p);
                    dealer.accept(s);
                }
            }
        }
    ```
    

    几乎就是一个 Select FROM WHERE 结构呀,那再看调用:

    ```Java
            personList.dealTheList(p -> p.getSex() == Person.Sex.FEMALE,
                    p -> String.valueOf(p.getHeight()), s -> System.out.println(s));
    ```
    

    条件:女性,查询:身高,处理:打印;

    有没有启发性??

    Java还支持stream(),专门提供一种串行的,更优雅的处理手段:

    ```Java
            personList.getPersons().stream()
                    .filter(p -> p.getSex() == Person.Sex.FEMALE)
                    .map(p -> p.getHeight()).forEach(h -> System.out.println(h));
    ```
    

    关于聚合运算,还可以参考Aggregate Operations

    小节

    总而言之,在只需要单个函数,特别是这个函数还只需一句话就搞定的时候,使用Lambda表达式可以极大的简化繁琐的定义,使代码更清晰、更侧重逻辑而非结构。

    算是一种编译型语言融合解释型语言有点的一种技巧吧。

    谢谢阅读。

  • 相关阅读:
    java面试之手写单例模式
    如何解决json返回的乱码
    ssm整合之applicationContext.xml
    ssm整合之springmvc.xml文件
    XML之MyBatis配置(1)
    XML之SpringMVC的基本配置
    使用eclipse出现错误:Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    Cannot find class [com.alibaba.druid.pool.DruidDataSuorce]
    eclipse如何关联Tomcat以及创建jsp实例
    新手如何快速使用Git
  • 原文地址:https://www.cnblogs.com/liuameng/p/3688888.html
Copyright © 2020-2023  润新知