个人笔记:关于java8 Lambda新特性

来源:互联网 发布:数据机房除尘 编辑:程序博客网 时间:2024/05/29 23:46

最近在研究Java8的新特性,Lambda(支持JDK8),虽然感觉有点晚,写这篇博客,这里对所学的知识进行一些整理,做个笔记,以便在今后的运用当中得到参考。

首先说几个概念

功能接口:只有一个抽象方法(功能)的接口,后面也叫接口函数。

针对上面的概念我想强调几点:

  1. 功能接口必须只有一个抽象方法;

  2. 功能接口中可以定义default方法,说到这里大家一定会疑惑,我给大家解释一下,java接口中的default方法也是在java8后引入的,它是在不破坏java现有实现架构的基础上往接口中添加新方法,也就是说java接口中可以有非抽象方法了,尽管如此,default方法也不适合过多定义,但是对于Java集合API配合Lambda表达式批处理,充分利用多核CPU并行操作来说至关重要。
  3. 有如:public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); },我们会说它不符合概念,抽象方法两个,注意,这里的equals方法实际上是Object的,所有的接口都会声明Object的equals方法,而大多数都是隐式的,所以像有的接口声明了equals方法不影响它是个功能接口。

Lambda表达式:表达式也称为闭包,是匿名类的一种简短的表示形式,语法简洁,本质上就是一个匿名方法。

注意哦,只是针对单一抽象方法接口(功能接口)也就是说,Lambda表达式的目标类型只能是符合功能接口特征的接口函数。

Lmabda 表达式的语法:(parameter list) -> {function body or expression}    括号内是参数列表,然后是符号 -> 再{},{}内是参数声明,表达式或代码块。举个例子:

原始写法:

public int add(int a,int b){

return a + b;

}

Lambda表达式:

 int add(int a,int b);  //功能接口函数

(int a,int b)-> {return a+b;}

或者

(a,b)->return a+b; //可以不写参数类型,Lambda可以通过上下文推断出来

也可以

(a,b)-> a+b; //当Lambda表达式的主体单一,可不写return,直接返回

Lambda表达式中的局部变量使用时注意以下几种情况:

1、局部变量可以在Lambda表达式作用域或者其作用域外声明,但使用之前必须先初始化,否则编译器会报错The local variable i may not have been initialized,如下图:

2、Lambda作用域中的变量禁止与其外部作用域的变量重名,那是因为Lambda不会定义新的作用域,Lambda作用域的与外部作用域是相同的,若声明了相同的变量,则编译器报Lambda expression’s local variable i cannot re-declare another local variable defined in an enclosing scope异常,如下图:


3、Lambda表达式中的变量使用必须为最终态等效终态,Lamdba表达式内只能对外部的变量进行引用而不可以赋值,否则会报

Variable i is required to be final or effectively final异常,如下图:

这里也涉及到一个概念就是捕获(Capture):它是用来解决Lambda表达式除了自己的参数变量以及内部定义的变量还可以引用哪些外部变量。

我们说Lambda表达式本质是一个匿名方法,跟内部类很相似,但是不同的是,非静态内部类总是对外部类持有一个强引用,因此传统的,当我们要引用外部类成员时我们必须将其声明为final。而Lambda则是,当表达式内引用到了外部成员或方法时才持有这个强引用。

Java8的一个新概念effectively  final,无论是Lamdba表达式或是内部类对象在引用外部成员时,外部成员可以声明为final也可以不声明。它会默认声明成effectively  final的成员。当在Lambda与内部类同时出现时,不论哪一个对外部成员赋值了,都会导致默认effectively  fina失效,编译出错。

Lambda表达式的目标类型推断:

Lambda可根据上下文推断目标类型,举个最通俗的例子,Runnable接口与Callable接口,有一下两个表达式:

         Runnable r = () ->{};  //
         Callable<Runnable> call = () ->{return () ->{};};  //②

Lambda会根据其参数列表跟返回值类型来区分其目标类型,由于代码①的参数列表为空,返回值为空,与Runnable接口的run()方法相同,自然判定目标类型为Runnable;

同理,代码②的参数列表为空,但返回值为其泛型类型,与Callable接口的call()方法类型相同,则判定其目标 类型为Callable。这里需要注意的是,return的语句可以是Lambda表达式,但是表达式方法的返回类型必须为功能接口。

Lambda表达式的"类型转换":

当我们在定义多个Lambda表达式往往会出现这种情况,由于Lambda的上下文推测,有时候表达式类型并不是很明确,看下面代码:


public static void main(String[] args) {
// TODO Auto-generated method stub
Lambda lambda = new Lambda();
/**
* getCount方法的Lambda表达式参数没法确定其目标类型,既可以是Operation接口函数,又符合Calculator接口函数
*/

lambda.getCount((int a,int b) -> {System.out.println("hhh");return a+b;});

}
private void getCount(Calculator cal) {
System.out.println("随便输出一句话...");
}
private void getCount(Operation ope){
System.out.println("我也随便好了...");
}

interface Operation{
int add(int a,int b);
}
interface Calculator{
int add(int a,int b);
}

注释已经说明了,Lambda的目标类型有多个,此时编译异常The method doPrivileged(PrivilegedAction) is ambiguous for the type AccessController,如下图:


这时我们可以根据需求进行类型转换,可以写成:


            lambda.getCount((Operation)(int a,int b) -> {System.out.println("hhh");return a+b;});//将其转换成我们需要的目标类型

Lambda表达式与集合类:

还是先介绍一个概念:java8为集合引入流(Stream)的概念,流的设计使用了管道(pipelines)模式。通常以一个集合对象为数据源进行链式操作。这里先不做举例了

Lambda表达式的其他用法:

Lambda表达式可以给一个函数接口赋值,比如:

      Runnable run = () ->{};

Lambda表达式必须先转换成接口函数才可以给Object引用赋值,例如:

      Object obj = (Runnable)() -> {};

但这种:

      Object obj = () -> {};编译是不通过的

Lambda的条件表达式:

boolean f = true;

Callable<Integer> cal = f ? () -> 11 : () -> 12;

Lambda表达式可通过方法引用调用现有方法,而不是匿名方法:

Collections.sort(list, (a,b)->a.toString().compareTo(b.toString()));

总结:还有很多值得研究探讨的地方,以后会补充修正




原创粉丝点击