Java8(1)之Lambda表达式初步与函数式接口

Lambda表达式初步

介绍

什么是Lambda表达式?java

在如 Lisp、Python、Ruby 编程语言中,Lambda 是一个用于表示匿名函数或闭包的运算符编程

为什么须要lambda表达式?闭包

  • 在 Java 中,咱们没法将函数做为参数传递给一个方法,也没法声明返回一个函数的方法。
  • 在 JavaScript 中,函数参数是一个函数,返回值是另外一个函数的状况是很是常见的;JavaScript 是一门很是典型的函数式语言。

看以下匿名内部类实例:编程语言

 1 import javax.swing.*;
 2 import java.awt.event.ActionEvent;
 3 import java.awt.event.ActionListener;
 4 
 5 public class SwingTest {
 6     public static void main(String[] args) {
 7         JFrame jFrame = new JFrame("My JFrame");
 8         JButton jButton = new JButton("My Button");
 9         // 匿名内部类
10         jButton.addActionListener(new ActionListener() {
11             @Override
12             public void actionPerformed(ActionEvent e) {
13                 System.out.println("Button Pressed");
14             }
15         });
16         jFrame.add(jButton);
17         jFrame.pack();
18         jFrame.setVisible(true);
19         jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
20     }
21 }
例 1:

如上代码很简单,经过第 10 行给第 8 行的按钮注册了一个监听器。而咱们知道,当事件触发真正起做用的其实是执行第 12 行的 actionPerformed 方法,为此咱们建立了 ActionListener 实例。ide

Lambda 表达式可让咱们只专一于方法的实现,在注册监听器时咱们能够忽略具体须要建立哪一个类型的实例。其实在 Idea 中自己就已经给咱们提示了,以下:函数式编程

即匿名内部类的写法是能够用 Lambda 表达式替换的,以下:函数

 1 import javax.swing.*;
 2 
 3 public class SwingTest {
 4     public static void main(String[] args) {
 5         JFrame jFrame = new JFrame("My JFrame");
 6         JButton jButton = new JButton("My Button");
 7         // Lambda 表达式
 8         jButton.addActionListener(event -> System.out.println("jButton Pressed"));
 9         jFrame.add(jButton);
10         jFrame.pack();
11         jFrame.setVisible(true);
12         jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
13     }
14 }
例 2:

在例 2 中经过第 8 行的 Lambda 表达式替换了例 1 中 10-15 行的匿名内部类写法,不只代码更简洁,并且含义更直观明了。ui

基本结构

在 Java 8 中 Lambda 表达式最原始的写法以下:this

(类型 prarm1,类型 param2,类型 param3, ...) -> {
// 方法体
}

能够将参数类型省略,编译器可自动推断:spa

(prarm1, param2, param3, ...) -> {
// 方法体
}

若是只有一个参数,还能够省略小括号:

prarm1 -> {
// 方法体
}

若是方法体只有一行代码,那么大括号也能够省略:

prarm1 -> <方法实现>

用例 2 中的 Lambda 为例演变过程以下:

// 原始写法
jButton.addActionListener((ActionEvent event) -> {
    System.out.println("jButton Pressed");
});
// 省略参数类型
jButton.addActionListener((event) -> {
    System.out.println("jButton Pressed");
});
// 省略小括号
jButton.addActionListener(event -> {
    System.out.println("jButton Pressed");
});
// 省略大括号
jButton.addActionListener(event -> System.out.println("jButton Pressed"));

函数式接口

介绍

在 Java 8 中, Iterable 接口中新增了一个默认函数 forEach 让咱们能够更方便的遍历它:

 1 package java.lang;
 2 
 3 import java.util.Iterator;
 4 import java.util.Objects;
 5 import java.util.Spliterator;
 6 import java.util.Spliterators;
 7 import java.util.function.Consumer;
 8 
 9 public interface Iterable<T> {
10     Iterator<T> iterator();
11 
12     default void forEach(Consumer<? super T> action) {
13         Objects.requireNonNull(action);
14         for (T t : this) {
15             action.accept(t);
16         }
17     }
18 
19     default Spliterator<T> spliterator() {
20         return Spliterators.spliteratorUnknownSize(iterator(), 0);
21     }
22 }
java.lang.Iterable

能够看到它接收一个 java.util.function.Consumer 类型参数,查看 JavaDoc:

该类上标注了一个 @FunctionalInterface 注解,而且注释中还说明了这个接口是一个函数式接口。查看该注解的 JavaDoc:

能够得到以下信息:

  • 这是一个信息性注解,用于标注在一个接口上使一个接口成为函数式接口。
  • 从概念上来讲,一个函数式接口有且只能有一个抽象方法。
  • 若是在接口中仅定义了一个抽象方法而且它覆盖了 java.lang.Object 类中公开的方法,那么这个接口就不是函数式接口。
  • 函数式接口的实例能够经过 Lambda 表达式、方法引用或构造方法引用来建立。
  • 若是被该注解标注的类型不知足下面两个条件,那么编译器将生成错误信息。
    1. 该类型是一个接口,不能够是注解类型、枚举、类。
    2. 被注解类型必须知足函数式注解的要求。
  • 若是一个接口知足了函数式接口的要求,但它没有标注该注解,编译器也会将它看做是函数式接口。

Lambda表达式的做用

看以下示例:

package zze.java8;

@FunctionalInterface
interface MyInterface {
    void test();
}

public class Test2 {
    public void test(MyInterface myInterface) {
        System.out.println("Test2.test");
        myInterface.test();
    }

    public static void main(String[] args) {
        Test2 test2 = new Test2();
        test2.test(() ->System.out.println("MyInterface.test"));
        /*
         * Test2.test
         * MyInterface.test
         */
    }
}

能够看到, Test2 的实例方法 test 其实是接收一个 MyInterface 对象,而 MyInterface 是一个函数式接口,因此咱们能够经过传入 Lambda 表达式做为 myInterface 参数,即:此时传入的 Lambda 表达式就是对函数函数式接口 MyInterface 的实现对象。因此上面代码其实也能够修改成以下:

Test2 test2 = new Test2();
MyInterface myInterface = () -> System.out.println("MyInterface.test");
test2.test(myInterface);
System.out.println(myInterface.getClass());
System.out.println(myInterface.getClass().getSuperclass());
System.out.println(myInterface.getClass().getInterfaces()[0]);
/*
Test2.test
MyInterface.test
class zze.java8.Test2$$Lambda$1/990368553
class java.lang.Object
interface zze.java8.MyInterface
*/

总结:

  • Lambda 表达式为 Java 添加了缺失的函数式编程特性,使咱们能将函数当作一等公民看待。
  • 在将函数做为一等公民的语言中(如 Python、JavaScript),Lambda 表达式的类型是函数。但在 Java 中,Lambda 表达式是一个对象,它们必须依附于一类特别的对象类型——函数式接口(functional interface)
相关文章
相关标签/搜索