lambda

来源:互联网 发布:尤克里里教程软件 编辑:程序博客网 时间:2024/06/03 06:38
一、定义
lambda表达式是java8新增特性,本质上是匿名方法,这个方法必须符合一个函数接口。
由三部分组成:参数列表、箭头、语句块。
Java的λ表达式只能用作赋值、传参、返回值等。

语法:
(parameters) -> expression 或 (parameters) ->{ statements; }

简单示例:
() -> 5                   // 不需要参数,返回值为 5 
x -> 2 * x                // 接收一个参数(数字类型),返回其2倍的值
(x, y) -> x – y           // 接受2个参数(数字),并返回他们的差值  
(int x, int y) -> x + y   //接收2个int型整数,返回他们的和 
(String s) -> System.out.print(s)  // 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)

类型:
lambada的类型叫做目标类型target type,其目标类型为函数接口function interface.


使用:
1. λ表达式主要用于替换以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等
2. λ表达式与集合类批处理操作(外部迭代到内部迭代)
//外部迭代:程序员实现
for(Object o:objects){
System.out.println(o);
}

//内部迭代:java自己实现
objects.foreach(System.out::println)


二、相关概念
* Function interface 函数接口:
一个接口中只有一个显式声明的抽象方法,那么它就是一个函数接口。
一般用@FunctionalInterface标注出来(也可以不标)。

举例:
Runnable r1 = () -> {System.out.println("Hello Lambda!");}; //Runnable是函数接口
public interface Callable<V> { V call() throws Exception; } //Callable是函数接口

使用:
Object obj = r1;
Object o = (Runnable) () -> { System.out.println("hi"); }; // correct 必须显示转换成函数接口
Object o = () -> {System.out.println("Hello Lambda!");};   // ERROR!

System.out.println( (Runnable)() -> {} ); // 正确
System.out.println( () -> {} );           // 错误! 目标类型不明

*Stream 流
一个流通常以一个集合类实例为其数据源,然后在其上定义各种操作。
流的API设计使用了管道(pipelines)模式,对流的一次操作会返回另一个流,
如同IO的API或者StringBuffer的append方法那样,从而多个不同的操作可以在一个语句里串起来。
  List<Shape> shapes = ...
    shapes.stream()
      .filter(s -> s.getColor() == BLUE)
      .forEach(s -> s.setColor(RED));
     
      //过程
      1.首先调用stream方法,以集合类对象shapes里面的元素为数据源,生成一个流。
      2.然后在这个流上调用filter方法,挑出蓝色的,返回另一个流。
      3.最后调用forEach方法将这些蓝色的物体喷成红色。
      (forEach方法不再返回流,而是一个终端方法,类似于StringBuffer在调用若干append之后的那个toString)


filter方法的参数是Predicate类型,forEach方法的参数是Consumer类型,它们都是函数接口,所以可以使用λ表达式。

还有一个方法叫parallelStream(),顾名思义它和stream()一样,只不过指明要并行处理,以期充分利用现代CPU的多核特性。
    shapes.parallelStream(); // 或shapes.stream().parallel()

* Capture 捕获
解决在λ表达式中我们可以使用哪些外部变量(即除了它自己的参数和内部定义的本地变量)的问题。
在Java8以前,如果要在内部类访问外部对象的一个本地变量,那么这个变量必须声明为final才行。
在Java8中,这种限制被去掉了,代之以一个新的概念,“effectively final”。
它的意思是你可以声明为final,也可以不声明final但是按照final来用,也就是一次赋值永不改变。
换句话说,保证它加上final前缀后不会出编译错误。

int tmp1 = 1; //包围类的成员变量
    static int tmp2 = 2; //包围类的静态成员变量
   
    public void testCapture() {
        int tmp3 = 3; //没有声明为final,但是effectively final的本地变量
        final int tmp4 = 4; //声明为final的本地变量
        int tmp5 = 5; //普通本地变量
        
        Function<Integer, Integer> f1 = i -> i + tmp1;
        Function<Integer, Integer> f2 = i -> i + tmp2;
        Function<Integer, Integer> f3 = i -> i + tmp3;
        Function<Integer, Integer> f4 = i -> i + tmp4;
        Function<Integer, Integer> f5 = i -> {
           tmp5  += i; // 编译错!对tmp5赋值导致它不是effectively final的
           return tmp5;
        };
        ...
        tmp5 = 9; // 编译错!对tmp5赋值导致它不是effectively final的
    }


* Method reference 方法引用
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。
方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。
计算时,方法引用会创建函数式接口的一个实例。

注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::"。

Integer::parseInt //静态方法引用  格式:ClassName::staticMethodName
    System.out::print //实例方法引用  格式:objectName::ClassMethodName
    Person::new       //构造器引用       格式:Class::new

* Default method 默认方法
接口里的方法加default修饰后,继承类不用随着新增default方法的修改而修改了。


三、注意的地方
1. 特殊包:
java.util.function
java.util.stream 

2. 常见的函数接口:
T - the type of the first argument to the function   //第一个参数
U - the type of the second argument to the function  //第二个参数
R - the type of the result of the function           //返回结果


//接收一个方法参数,返回一个结果。
    public interface Function<T, R> {  
        R apply(T t);
    }

//接收一个方法参数,不返回结果。
    public interface Consumer<T> {
        void accept(T t);
    }
   
    //用来判断某项条件是否满足。经常用来进行筛滤操作 
//Represents a predicate (boolean-valued function) of one argument. 
    public interface Predicate<T> {
        boolean test(T t);
    }
   
    //比较
public interface Comparator<T>{
int compare(T o1,

            T o2)

}

   
//生产者:一个参数
//Represents a supplier of results. 
public interface Supplier<T>{
T get()
}

//生产者:两个参数
//Applies this function to the given arguments
Interface BiFunction<T,U,R>{
R apply(T t,
        U u)
}

//线程运行
Interface Runnable{
void run()
}

3. 几个终端方法:
foreach,
toarray,
collect,

reduce


参考:
http://blog.csdn.net/ioriogami/article/details/12782141/
http://blog.csdn.net/renfufei/article/details/24600507
https://www.cnblogs.com/xiaoxi/p/7099667.html