JDK1.8新特性-lambda表达式| 8月更文挑战

193 阅读3分钟

这是我参与8月更文挑战的第11天

为什么要使用lambda表达式

Java 对象往往比较“重量级”:实例化一个类型往往会涉及不同的类,并需要初始化类里的字段和方法。

不过有些 Java 对象只是对单个函数的封装。例如下面这个典型用例:Java API 中定义了一个接口(一般被称为回调接口),用户通过提供这个接口的实例来传入指定行为,例如:

public interface ActionListener {
  void actionPerformed(ActionEvent e);
}

因为该方法只在调用处使用一次,所以我们并不需要去实现这个类,在java8之前,我们一般都是使用匿名内部类的方式去实现该方法:

button.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    ui.dazzle(e.getModifiers());
  }
});

缺点:

  • 语法过于冗余
  • 匿名类中的 this 和变量名容易使人产生误解
  • 类型载入和实例创建语义不够灵活
  • 无法捕获非 final 的局部变量
  • 无法对控制流进行抽象

因此,jkd8中引入lambda表达式很好的解决了这一个问题


lambda表达式的引入

对接口的要求:

虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。

jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用

lambda表达式语法:

(参数列表)->{方法体}

其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符

lambda表达式强调可推导、可省略,所以见到大部分lambda表达式都是 变量->方法体 或 类名::方法

lambda表达式结构:

下面是一些lambda表达式实例:

(int x, int y) -> {return x + y;}
() -> {return 42;}
(String s) -> { System.out.println(s); }

不讨论方法及接口名,上述表达式对应java8之前代码为:

int 方法名  (int x, int y){
    return x+y;
}

int 方法名 (){
    retrun 42;
}

void 方法名(String s){
    System.out.println(s);
}

如果方法体只有一条语句时,可以省略大括号,上述表达式为:

(int x, int y) -> return x + y;
() -> return 42;
(String s) -> System.out.println(s); 

如果方法体只有一条语句且是return语句时,可以省略retrun和分号,优化过的表达式为:

(int x, int y) -> x + y
() -> 42
(String s) -> System.out.println(s);

return和分号必须一起省略

是不是感觉清晰了很多,但这只是lambda表达式最基本的表达形式,lambda可以通过目标类型的上下文来推导数据类型,比方说上述的第一个方法就可以简化为:

(x,y)->x+y

如果数据类型不写,则所有的变量都不写,不能写作(x,int y)->x+y这种。

当参数列表有且只有一个参数时,参数括号可以省略,例如

x->x+2

Lambda 表达式常用示例

有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。

语法:

目标引用::方法 静态方法的归属者为类名,普通方法归属者为对象

双冒号 :: person::getName(),等同于person.getName(),表示对象引用方法