jdk8中的lambda表达式

什么是lambda表达式?

lambda表达式又称闭包,允许将一个函数当作方法的参数来传递(传递函数)或者说把代码当作数据来传递。 

长啥样?

如下两个示例,示例1为非lambda表达式实现的形态(多行代码),示例2为使用lambda表示式后的样子(1行代码)。
功能:实现一个过滤符合指定条件的人群
示例1:

interface CheckPerson { //步骤1.定义接
    boolean test(Person p);     
}

class CheckPersonEligibleForSelectiveService implements CheckPerson {//步骤2. 实现类
public boolean test(Person p) {
    return p.gender == Person.Sex.MALE &&
        p.getAge() >= 18 &&
        p.getAge() <= 25;
}
}
printPersons(roster, new CheckPersonEligibleForSelectiveService());//步骤3.调用

示例2:

printPersons(roster,
    p -> p.getGender() == Person.Sex.MALE
    && p.getAge() >= 18
    && p.getAge() <= 25);      
为啥要用lambda表达式?

如上示例,显然使用lambda表示式的形式更加简洁,漂亮。当然,lambda表达式带来的好处远不如此,其在并发编程中带来的优势更是不言而喻。 

拿个官方栗子来看看  
  1. 打印年龄大于指定值的成员信息 

    public static void  printPersonsOlderThan(List<Person> roster, int age) {
        for (Person p : roster) {
            if (p.getAge() >= age) {
                p.printPerson();
            }
        }
    }

    上述示例会随着功能或者数据结构类型(int 变更为 double)的改变显得稳定性较差
    如需求为年龄在指定范围内,则实现方法为:

    public static void printPersonsWithinAgeRange(
        List<Person> roster, int low, int high) {
            for (Person p : roster) {
                if (low <= p.getAge() && p.getAge() < high) {
                p.printPerson();
            }
        }
    }
  2. 为支持多样的需求,对过滤条件做抽象,如下 

    public static void printPersons(
        List<Person> roster, CheckPerson tester) {
        for (Person p : roster) {
            if (tester.test(p)) {
                p.printPerson();
            }
        }
    }

为支持上述设计做如下实现:见示例1。(接口,本地实现类,调用) 对示例1使用匿名类做简化(省去本地实现类CheckPersonEligibleForSelectiveService)后,代码如下:

printPersons(roster,
    new CheckPerson() {
    public boolean test(Person p) {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }
});  

如此代码简化为 接口+匿名类调用
3. 使用lambda表达式做简化 由于CheckPerson接口为函数接口(仅有一个抽象类),可以对使用lambda表示式代替匿名函数 

printPersons(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);      

4.继续简化。使用标准函数接口

interface CheckPerson {
    boolean test(Person p);
} 

对于此类函数接口(含有一个入参和boolean返回值),表现实现相对简单。由于此类接口过于简单,以至于没有必要在自己的应用中定义,所以jdk8在java.util.function包提供了几个标准的函数接口以方便用户使用。
对于此例接口(含有一个入参和boolean返回值)可以使用Predicate  接口代替 CheckPerson 

interface Predicate<T> {
    boolean test(T t);
}

由此,该例本地实现类可变为 

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
} 

5.尽可能多的使用lambda表达式 

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
} 

如上所示,该功能为对符合条件的对象做动作(打印p.printPerson),如果需求变更为仅打印名称或仅打印email呢?是不是要重新在写相应的代码逻辑呢?代码就这样膨胀了。
然而使用lambda表达式可以在调用时指定具体的动作,无需写冗长的匿名类。如此使用lambda表达式来来实现动作的描述
对于p.printPerson()此类方法--需要一个参数Person对像且无返回值,可使用标准函数接口Consumer(抽象方法为accept)代替,如此待接口定义做进一步优化 

public static void processPersons(
    List<Person> roster,
    Predicate<Person> tester,
    Consumer<Person> block) {
        for (Person p : roster) {
            if (tester.test(p)) {
                block.accept(p);
            }
        }
}

实例调用,此时完成将 printPerson的动作转换为lambda表示式的形式

processPersons(
     roster,
     p -> p.getGender() == Person.Sex.MALE
         && p.getAge() >= 18
         && p.getAge() <= 25,
     p -> p.printPerson()
);

此过程中操作对象为person, 如果想操作其中的成员变量呢,我们需要通过对象获取该对象的成员变量,即需要一个函数接口支持一个入参和返回。标准函数接口中Function可以支持,抽象方法为apply, 如此我们可以使用该接口,实现map映射(通过person获取对应成员变量),函数定义如下: 

public static void processPersonsWithFunction(
    List<Person> roster,
    Predicate<Person> tester,
    Function<Person, String> mapper,
    Consumer<String> block) {
    for (Person p : roster) {
        if (tester.test(p)) {
            String data = mapper.apply(p);
            block.accept(data);
        }
    }
}

实例调用

processPersonsWithFunction(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);  

6.使用泛型 使用泛型重新定义processPersonsWithFunction 

public static <X, Y> void processElements(
    Iterable<X> source,
    Predicate<X> tester,
    Function <X, Y> mapper,
    Consumer<Y> block) {
    for (X p : source) {
        if (tester.test(p)) {
            Y data = mapper.apply(p);
            block.accept(data);
        }
    }
}

实例调用 

processElements(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);  

如上调用依次做如下操作:
1)从集合roster依次取出元素
2)做条件过滤
3)做map影射,由对象获取成员变量
4)做动作,打印成员变量 

如上使用到的几个标准函数接口如下 

函数接口签名要求
Predicate一个参数和boolean返回值
Function一个参数和一个返回值
Consumer一个入参,无返回值

7.将lambda表达式做为参数做聚合操作
如下为对符合指定条件的人群打印其emalAddress 

roster
    .stream()
    .filter(
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25)
    .map(p -> p.getEmailAddress())
    .forEach(email -> System.out.println(email));   

其中各聚合操作对应的意义如下 

聚合操作意义
Stream<E> stream()获取顺序对象
Stream<T> filter(Predicate<? super T> predicate)过滤获取匹配元素
<R> Stream  map(Function<? super T,? extends R> mapper) 将一个对应映射为另一个值
void forEach(Consumer<? super T> action)遍历并执行Consumer对象对应的动作


总体说来,lambda表达式在简化代码的同时,使应用对易变的需求有更好的适应性,同时并发操作使其可更好的利用硬件资源。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值