专栏首页技术与生活Lambda 深入浅出

Lambda 深入浅出

在学习 Lambda之前我们得学习下通过行为参数化传递代码,如何理解?举个例子就是,在我们进行应用程序开发的时候,有时候需求是不停的增加和修盖的,如“我们需要寻找某个苹果仓库里面的红苹果”,又变成“需要寻找重量大于300g 的苹果”等等,这个时候我们如果按照之前的思路来就会变得臃肿以及后续难易维护。我们上干货。

好吧,先开始第一个需求,我们要寻找红苹果,不要绿苹果 先定义苹果类

public class Apple {    
    private Integer weight;    private String color;    public Integer getWeight() {        return weight;
    }    public void setWeight(Integer weight) {        this.weight = weight;
    }    public String getColor() {        return color;
    }    public void setColor(String color) {        this.color = color;
    }    public Apple(Integer weight, String color) {        super();        this.weight = weight;        this.color = color;
    }    
    public Apple() {        // TODO Auto-generated constructor stub
    }    @Override
    public String toString() {        return "Apple [weight=" + weight + ", color=" + color + "]";
    }

}

之后我们轻而易举的写下寻找苹果的代码

public class Demo1 {    public static void main(String[] args) {
        List <Apple> apples=new ArrayList<Apple>();
        apples.add(new Apple(10,"red"));
        apples.add(new Apple(15,"red"));
        apples.add(new Apple(8,"green"));
        
        List<Apple> filterApples=new ArrayList<Apple>();        
        for(Apple apple:apples){            if("red".equals(apple.getColor())){
                filterApples.add(apple);
            }
        }        
        for(Apple apple:filterApples){
            System.out.println(apple);
        }
        
    }

}

非常简单,那么问题来了,如果这个时候需求变动,要求选择的是红色的并且重量大于10的,那么怎么办。小 case,不就一行代码的事

if("red".equals(apple.getColor()) && apple.getWeight>10){
    filterApples.add(apple);
}

不错,的确做出来了,这个时候是不是心里有点虚,那么又有新的变动咋办,每次修改类重新部署这开销有点大啊。这个时候,我们想到了设计模式的对修改关闭对新增开发的开闭原则。

好吧,既然想到了就开始实现吧,先构建一个接口来做所有苹果的判断

public interface ApplePredicate {    public boolean test(Apple apple);
}

接着把需要实现都写出来,这样不管有什么新的需求就都可以直接新增,这里先把颜色和重量的实现下

public class RedApplePredicate implements ApplePredicate{    @Override
    public boolean test(Apple apple) {        return "red".equals(apple.getColor());
    }
}
public class WeightApplePredicate implements ApplePredicate{    @Override
    public boolean test(Apple apple) {        return apple.getWeight()>10;
    }
}

接下来写个方法,来进行判断

public static List<Apple> filterApples(List<Apple> apples,ApplePredicate predicate){
        List<Apple> filterApples=new ArrayList<Apple>();        for(Apple apple:apples){            if(predicate.test(apple)){
                filterApples.add(apple);
            }
        }        return filterApples;
    }

到此为止,我们很好的把变动的部分单独抽取出来,我们的应用如下

public static void main(String[] args) {
        List <Apple> apples=new ArrayList<Apple>();
        apples.add(new Apple(10,"red"));
        apples.add(new Apple(15,"red"));
        apples.add(new Apple(8,"green"));
        
        List<Apple> filterApples=filterApples(apples,new WeightApplePredicate());        
        for(Apple apple:filterApples){
            System.out.println(apple);
        }
    }

想要什么样的 Prdicate 就在代码List<Apple> filterApples=filterApples(apples,new WeightApplePredicate());处修正自己的Prdicate实现。

更有甚者,可以用匿名类代替new WeightApplePredicate()的实现。

List<Apple> filterApples=filterApples(apples,new ApplePredicate(){  @Override
    public boolean test(Apple apple) {        return apple.getWeight()>10;
    }
});

这样看来貌似不错,但是说到现在貌似和 Lambda 一点关系都没有,的确,这才是开始,我们的 Lambda终于要上场了。

就在我们要传递new WeightApplePredicate()的时候,并且这个类的接口有唯一的方法,那么这个接口可以说是函数式接口,可以使用标注@FunctionalInterface在接口上进行标注,这样有错误在编译的时候就能发现。

有了 Lambda 的表达式,我们可以这样写

List<Apple> filterApples=filterApples(apples,(Apple apple)->"red".equals(apple.getColor()));

Lambda 表达式有3块,分别是参数、箭头、主题

形如(parameters) -> expression 或者 (parameters) -> { statements; } 仔细观察他们的区别,不在花括号内的是表达式;花括号里面是声明语句,有返回值需要 return 关键字

参数就是(Apple apple) 箭头就是 -> 主体部分是"red".equals(apple.getColor())

Lambda使用场景:在函数式接口的地方使用


一些JDK内置的函数式接口有如下 Predicate:java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型 T对象,并返回一个boolean。 Consumer:java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用 这个接口。 Function:java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个 泛型T的对象,并返回一个泛型R的对象。 Supplier:Supplier<T>具有唯一一个抽象方法叫作get,代表的函数描述符是()-> T。


Lambda 还可以进行类型推断 比如我们的List<Apple> filterApples1=filterApples(apples,a->"red".equals(a.getColor())); 这里的 a 变量会自己推断类型为 Apple


关于变量,Lambda 里面如果需要接受局部的参数变量,那么该参数必须是 final 的,为什么?

  • 第一,实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了 这个限制。
  • 第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式.

关于 Lambda 的方法引用见下次了 _

本文分享自微信公众号 - 技术与生活(technology_life),作者:唐顺

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-09-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计模式-责任链模式

    看下具体的代码: 先把抽象处理者写出来,他是一个抽象类或者接口,这里使用抽象类,每个处理者给一个名字 name 属性

    breezedancer
  • Spring Boot的过滤器

    其中1代表是微服务启动类,2代表在启动类下面构建一个包,再在堡垒新建一个过滤器类,并且实现 Filter 接口

    breezedancer
  • 设计模式-观察者模式

    软件系统很多时候需要这样的情况,一旦发生故障,就通知对应的系统或模块,使之能够及时处理。

    breezedancer
  • 使用supervisor对程序的输出重定向

    超级大猪
  • Hive-基础

    DataScience
  • 手机纷纷启用代言人,粉丝经济进入3.0时代

    近日举行的荣耀8新品发布会上,最吸引眼球了除了主打颜值的产品本身之外,还有一位高颜值的明星:小鲜肉吴亦凡成为荣耀品牌中国区代言人。吴亦凡未到场,然而一夜之间其粉...

    罗超频道
  • Android 高仿微信发朋友圈浏览图片效果

    版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/50...

    Hankkin
  • java 避免出现NullPointerException(空指针)的方法总结

    Java应用中抛出的空指针异常是解决空指针的最好方式,也是写出能顺利工作的健壮程序的关键。俗话说“预防胜于治疗”,对于这么令人讨厌的空指针异常,这句话也是成立的...

    林老师带你学编程
  • 学习SpringBoot系列 -02- 多数据源

    许杨淼淼
  • SAP成都研究院CEC团队三巨头之一:M君的文章预告

    国人总倾向于把特点或者作用类似的人或物放在一起比较并做出排名,于是就有了许多“某某某三巨头”的称谓。

    Jerry Wang

扫码关注云+社区

领取腾讯云代金券