前段时间的面试基本结束了,最后也有了不错的结果,之后一段时间到入职打算好好整理一些东西。
想到马上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,方法引用,目标类型和默认方法)
原理
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());
}
}
关于性能
16页讲到最差(capture)也和inner class一样, non-capture好的情况是inner class的5倍
但是在使用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