关于Lambda表达式的整理

作者: kachidokima | 来源:发表于2017-05-09 17:54 被阅读128次

    前段时间的面试基本结束了,最后也有了不错的结果,之后一段时间到入职打算好好整理一些东西。
    想到马上Java9也要出来了,Android也马上支持Java8,自己都没有好好整理过java8比较重要的知识点,可以说8很多改动都是为了Lambda服务的,所以整理一下Lambda的内容。

    关于闭包

    首先闭包是指,将当前作用域中的变量通过值或者引用的方式封装到lambda表达式中,成为表达式的一部分,它使你的lambda表达式从一个普通的函数变成带隐藏参数的函数,当然闭包也可以不通过lambda实现

    简单理解为闭包就是定义在函数内部的函数

    Lambda 表达式与匿名类的区别

    • Lambda 表达式与匿名类的主要不同在于两者的编译方法
    • 对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类

    什么时候用

    任何可以接受一个函数式接口的地方都可以用lambda表达式
    lambda作用在于

    • 逻辑更紧凑
    • 引入闭包,更简洁的实现闭包
    • 允许函数方法作为对象传递

    函数式接口

    定义

    所谓的函数式接口,也叫SAM接口(Single Abstract Method interfaces),当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法

    • 函数式接口里允许定义默认方法
    • 函数式接口里允许定义静态方法
    • 函数式接口里允许定义java.lang.Object里的public方法
        //以下都是不会报错的
        @FunctionalInterface
        interface GreetingService{
            void sayMessage(String message);
    
            default void doSomeMoreWork1()
            {
                // Method body
            }
            static void printHello(){
                System.out.println("Hello");
            }
            @Override
            boolean equals(Object obj);
        }
    

    @FunctionalInterface注解

    加不加@FunctionalInterface对于接口是不是函数式接口没有影响,只是提醒编译器去检查该接口是否仅包含一个抽象方法,用于编译级错误检查

    新增java.util.function

    • Predicate:用于判断一个对象是否满足某种条件,只有一个test抽象函数,接受一个泛型T对象返回boolean
    • Consumer:用于对对象进行消费操作,只有一个accept抽象函数,接受一个泛型对象无返回
    • Function:用于对象的转换,只有一个apply抽象函数,接受一个泛型T,返回一个泛型R
    • Supplier:用于创建对象
    • 还有一些衍生的可以自己看包下源码

    使用

    使用相信大家都会一些,我就不列举,网上大把的文章,下面两篇总结不错

    深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    10个实例表达式

    原理

    Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有静态函数

    • 它使用 Java 7 中新加的 invokedynamic,运行时调用LambdaMetafactory.metafactory动态的生成内部类,实现了接口
    • 生成一个类私有静态函数,在生成的实现类中调用

    关于 Java 如何将 Lambda 表达式编译为字节码

    国外的一篇写的不错

    通俗易懂的解释

    interface Print<T> {
        public void print(T x);
    }
    public class Lambda {   
        public static void PrintString(String s, Print<String> print) {
            print.print(s);
        }
        public static void main(String[] args) {
            PrintString("test", (x) -> System.out.println(x));
        }
    }
    

    通过编译最终等价于

    interface Print<T> {
        public void print(T x);
    }
    public class Lambda {   
        public static void PrintString(String s, Print<String> print) {
            print.print(s);
        }
        private static void lambda$0(String x) {
            System.out.println(x);
        }
        final class $Lambda$1 implements Print{
            @Override
            public void print(Object x) {
                lambda$0((String)x);
            }
        }
        public static void main(String[] args) {
            PrintString("test", new Lambda().new $Lambda$1());
        }
    }
    

    关于性能

    Oracle写的lambda表现文档

    16页讲到最差(capture)也和inner class一样, non-capture好的情况是inner class的5倍

    image1image1

    但是在使用Stream的时候并不是所有情况都比普通迭代快的

    下面这篇文章比较了各种情况下imperative code与functional code的性能表现

    The effects of programming with Java 8 Streams on algorithm performance

    关于Streams

    Lambda最佳结合应该就是Stream了,函数式编程与简洁的结合
    Java 8的Stream内置了许多类似于数据库的操作filter、sort、map、reduce等
    用法就不贴了,大把文章
    官方的在这里

    Stream优点:

    • 以数据库操作数据的方式,专注于如何做这个某个步骤,表达式的方式

    • 高并发(看到map、reduce就应该能想到了)

    刚开始看Stream感觉和RxJava非常像,但是仔细思索后有发现其实是两个不同的东西,只是长得像而已。
    下面这个最高票回答总结的非常好,总体来说

    • stream是单次使用,是基于被动PULL
    • rx是基于观察者模式,可多次订阅,是基于主动PUSH

    Stream与Rxjava的不同

    相关文章

      网友评论

        本文标题:关于Lambda表达式的整理

        本文链接:https://www.haomeiwen.com/subject/kvkitxtx.html