lambada表达式总结

前言

作为jdk1.8的新特性,8的标准库都开始大量使用lambda表达式了,你有什么理由不去学习lambda,这么简洁,这么爽的一种编程方法,不学不觉得可惜吗?

lambda即λ,是匿名函数的意思,在java中,lambda表达式本质上是定义一个匿名内部类而已。即定一个一匿名函数的同时,定义一个匿名内部类。

1、匿名内部类->匿名函数

匿名内部类,可以对接口和抽象类进行使用,直接生成其子类或实现类对象。

现有一个接口,其内有一个抽象方法。

public interface Ability {
    void doAbility(String token);
}

当我们想用匿名内部类的时候,即可以不通过定义子类或实现类,直接获得子类对象或实现类对象,如下:

public class LOLHero  {
    public static void main(String[] args) {

        Ability ability = new Ability(){
            @Override
            public void doAbility(String token) {
                System.out.println("我只是一个栗子");
            }
        };

    }
}

哈哈,下面用lambda实现同样的效果,这个例子就可以看到lambda的本质了,颤抖吧,小伙伴们:

public class LOLHero  {
    public static void main(String[] args) {
        Ability ability2 = (token)->{System.out.println("我也只是一个栗子");};
    }
}

是的,相信你已经看出来了,lambda表达式表达的就是一个匿名内部类,不过不是通过new一个接口或抽象类,而是直接定义里面方法,对,就是匿名方法,来表达一个匿名内部类。所以前面说,lambda表达式就是一种使用匿名内部类的简化方法。不同点在于,匿名内部类使用范围必须是接口和抽象类,且接口和抽象类内可以有n个抽象方法+n个具体方法,但是lambda表达式适用范围仅仅是接口,且接口内只能有1个抽象方法+n个具体方法。没错,这个抽象方法就是你定义的匿名函数。

2、lambda两种使用场景

lambda表达式的使用,是java通过对于上下文语境进行推断,得到接口类型,若发现接口只有一个抽象方法,就可以使用lambda表达式。

第一个场景,形参位置:

public class LOLHero  {
    public static void main(String[] args) {
        specialFuntion((token)->{
            System.out.println(token+":万箭齐发。。。。。。");
        });
    }

    public static void specialFuntion(Ability ability){
        ability.doAbility("寒冰射手");
    }
}

java可以根据形参的数据类型来推断lambda表达式。在main方法中,我们调用specialFunction方法的时候,要传入一个匿名内部类,由于机智的你发现这个接口只有一个抽象方法,因此我们直接用lambda表达式,直接定义匿名函数,就可以拿到这个匿名内部类,至于接口名,接口抽象方法的形参类型,java都可以推断出来,你就不用填写啦。嗯。。。。也许你会说是,语法糖。。。。嘛。是的,但是像是魔法一样有趣。

运行结果:

寒冰射手:万箭齐发。。。。。。

第二个场景,返回值位置:

public class LOLHero  {
    public static void main(String[] args) {
        Ability ability1 = getAbility();
    }

    public static Ability getAbility(){
        return (token)->{
            System.out.println(token+":万箭齐发。。。。。。。。");
        };
    }
}

这种情况下,java会根据返回值的数据类型来推断lambda表达式。道理是一样的,java知道要返回一个Ability接口,而该接口仅有一个抽象方法,因此在这里机智的你可以使用lambda表达式来代表一个匿名内部类。

3、lambda使用举例

线程启动

public class LOLHero  {
    public static void main(String[] args) {
        ScheduledExecutorService es = Executors.newScheduledThreadPool(2);
        es.scheduleAtFixedRate(()->{
            System.out.println("我是栗子!");
        },0,2,TimeUnit.SECONDS);

    }
}

第一个参数是Rnnable接口,这个接口只有一个抽象方法,因此此处可以使用lambda表达式。

流对象之遍历

机智的你们都知道,在实际使用集合时,最常用的时遍历迭代每一个元素,对每一个元素进行操作。而使用集合的流对象+lambda表达式,可以很方便的做到这一点。

public class LOLHero  {
    public static void main(String[] args) {
        List heros = new ArrayList<>(Arrays.asList("爱希","蛮王","易大师","皇子","皇子","皇子","我"));
        heros.stream().forEach(x-> System.out.println(x));
    }
}

该处,forEach方法中形参是Consumer接口,他有唯一的抽象方法accept(T t),此处lambada表达式就是一个实现了Consumer接口的匿名内部类。遍历结果:

爱希
蛮王
易大师
皇子
皇子
皇子
我

机智的你,想过滤重复的数据,和筛选一定条件的数据,如下:

public class LOLHero  {
    public static void main(String[] args) {
        List heros = new ArrayList<>(Arrays.asList("爱希","蛮王","易大师","皇子","皇子","皇子","我"));
        heros.stream().filter(n->n.length() > 1).distinct().forEach(n-> System.out.println(n));
    }
}

filter方法的形参是Predicate接口,有唯一的抽象方法test(T t),用于筛选长度>1的元素,distinct()进行过滤重复元素,遍历结果如下:

爱希
蛮王
易大师
皇子

流对象之函数式编程

函数式编程的核心是map进行元素转换,reduce进行元素聚合,如下:

public class LOLHero  {
    public static void main(String[] args) {
        List heros = new ArrayList<>(Arrays.asList("爱希","蛮王","易大师","皇子","皇子","皇子","我"));
        Integer reduce = heros.stream().map((x) -> x.length()).reduce(0, (sum, x) -> sum + x);
        System.out.println(reduce);
    }
}

map方法的形参是Function接口,其有唯一的抽象方法apply(T t),reduce方法的第一个参数是初始值,第二个参数为接口BinaryOperator,其有唯一抽象方法apply(T t,U u),reduce方法会把前面元素的计算结果放在sum位置(第一次放初始值),把下一个元素放在x位置进行聚合操作。该例子中,map将每个元素转化成其长度,用reduce获取其总长度,结果如下:

14

下面还有几个用函数编程的例子,大家可以自行探索:

public class LOLHero  {
    public static void main(String[] args) {
        List heros = new ArrayList<>(Arrays.asList("爱希","蛮王","易大师","皇子","皇子","皇子","我"));
        //按一定格式将集合转化成字符串
        String s = heros.stream().map(x->x+1).collect(Collectors.joining(","));
        //将集合转化后并获取
        List list = heros.stream().map(x -> x + x).collect(Collectors.toList());
        //获取int,long,double的统计对象,可以获取最大值,最小值,平均值,总和等等,十分方便
        IntSummaryStatistics intSummaryStatistics = heros.stream().map(x -> x.length()).mapToInt(x -> x).summaryStatistics();
    }
}

总结

说了这么多,其实lambda的本质,还是像开头说的那样,定义一个匿名内部类而已,只要抓住这个关键,使用lambda便是游刃有余啊。

java8新增四大核心函数接口(函数接口,即只有一个抽象函数的接口):

Consumer : 消费型接口(有进无出)
         void accept(T t);
 Supplier : 供给型接口(无进有出)
         T get();
         
 Function : 函数型接口(有进有出)
        R apply(T t);
        
 Predicate : 断言型接口(有进有出)
        boolean test(T t);
        

 

你可能感兴趣的:(编程语言)