Lambda 表达式基础理论与示例

Lambda 表达式基础理论与示例

Lambda 表达式,也可称为闭包,推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

lambda 表达式的语法格式如下:

(parameters) -> expression

(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

可选的返回关键字:如果主体只有一个表达式返回值,编译器会自动返回值,大括号需要指定表达式返回了一个数值。

Lambda 表达式实例

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5 

() -> 5 

// 2. 接收一个参数(数字类型),返回其2倍的值 

x -> 2 * x 

// 3. 接受2个参数(数字)返回差值 

(x, y) -> x – y 

// 4. 接收2个int型整数,返回和 

(int x, int y) -> x + y 

// 5. 接受一个 string 对象在控制台打印,不返回任何值(看起来像是返回void) 

(String s) -> System.out.print(s)

在 Java8Tester.java 文件输入以下代码:

Java8Tester.java 文件

public class Java8Tester {

   public static void main(String args[]){

      Java8Tester tester = new Java8Tester();

      // 类型声明

      MathOperation addition = (int a, int b) -> a + b;

      // 不用类型声明

      MathOperation subtraction = (a, b) -> a - b;

      // 大括号中的返回语句

      MathOperation multiplication = (int a, int b) -> { return a * b; };

      // 没有大括号及返回语句

      MathOperation division = (int a, int b) -> a / b;

      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));

      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));

      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));

      System.out.println("10 / 5 = " + tester.operate(10, 5, division));

      // 不用括号

      GreetingService greetService1 = message ->

      System.out.println("Hello " + message);

      // 用括号

      GreetingService greetService2 = (message) ->

      System.out.println("Hello " + message);

      greetService1.sayMessage("Runoob");

      greetService2.sayMessage("Google");

   }

   interface MathOperation {

      int operation(int a, int b);

   }

   interface GreetingService {

      void sayMessage(String message);

   }

   

   private int operate(int a, int b, MathOperation mathOperation){

      return mathOperation.operation(a, b);

   }

}

执行以上脚本,输出结果为:

$ javac Java8Tester.java

$ java Java8Tester

10 + 5 = 15

10 - 5 = 5

10 x 5 = 50

10 / 5 = 2

Hello Runoob

Hello Google

使用 Lambda 表达式需要注意以下两点:

Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,使用各种类型的Lambda表达式,定义MathOperation接口的方法。然后定义了sayMessage的执行。

Lambda 表达式免去了使用匿名方法的麻烦,给予Java简单但是强大的函数化的编程能力。

变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

在 Java8Tester.java 文件输入以下代码:

Java8Tester.java 文件

public class Java8Tester {

   final static String salutation = "Hello! ";

   public static void main(String args[]){

      GreetingService greetService1 = message ->

      System.out.println(salutation + message);

      greetService1.sayMessage("Runoob");

   }

   

   interface GreetingService {

      void sayMessage(String message);

   }

}

执行以上脚本,输出结果为:

$ javac Java8Tester.java

$ java Java8Tester

Hello! Runoob

可以直接在 lambda 表达式中访问外层的局部变量:

Java8Tester.java 文件

public class Java8Tester {

    public static void main(String args[]) {

        final int num = 1;

        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));

        s.convert(2);  // 输出结果为 3

    }

    public interface Converter<T1, T2> {

        void convert(int i);

    }

}

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

int num = 1; 

Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));

s.convert(2);

num = 5; 

//报错信息:Local variable num defined in an enclosing scope must be final or effectively

 final

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

String first = ""; 

Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错

 

Java的方法分为实例方法,例如Integer定义的equals()方法:

 

public final class Integer {

    boolean equals(Object o) {

        ...

    }

}

以及静态方法,例如Integer定义的parseInt()方法:

public final class Integer {

    public static int parseInt(String s) {

        ...

    }

}

无论是实例方法,还是静态方法,本质上都相当于过程式语言的函数。例如C函数:

char* strcpy(char* dest, char* src)

只不过Java的实例方法隐含地传入了一个this变量,即实例方法总是有一个隐含参数this。

函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。历史上研究函数式编程的理论是Lambda演算,所以经常把支持函数式编程的编码风格称为Lambda表达式。

Lambda表达式

在Java程序中,经常遇到一大堆单方法接口,即一个接口只定义了一个方法:

Comparator

Runnable

Callable

以Comparator为例,想要调用Arrays.sort()时,可以传入一个Comparator实例,以匿名类方式编写如下:

String[] array = ...

Arrays.sort(array, new Comparator<String>() {

    public int compare(String s1, String s2) {

        return s1.compareTo(s2);

    }

});

上述写法非常繁琐。从Java 8开始,可以用Lambda表达式替换单方法接口。改写上述代码如下:

// Lambda

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {

        String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };

        Arrays.sort(array, (s1, s2) -> {

            return s1.compareTo(s2);

        });

        System.out.println(String.join(", ", array));

    }

}

 Run

观察Lambda表达式的写法,它只需要写出方法定义:

(s1, s2) -> {

    return s1.compareTo(s2);

}

其中,参数是(s1, s2),参数类型可以省略,因为编译器可以自动推断出String类型。-> { ... }表示方法体,所有代码写在内部即可。Lambda表达式没有class定义,因此写法非常简洁。

如果只有一行return xxx的代码,完全可以用更简单的写法:

 

Arrays.sort(array, (s1, s2) -> s1.compareTo(s2));

返回值的类型也是由编译器自动推断的,这里推断出的返回值是int,因此,只要返回int,编译器就不会报错。

FunctionalInterface

把只定义了单方法的接口称之为FunctionalInterface,用注解@FunctionalInterface标记。例如,Callable接口:

@FunctionalInterface

public interface Callable<V> {

    V call() throws Exception;

}

再来看Comparator接口:

@FunctionalInterface

public interface Comparator<T> {

    int compare(T o1, T o2);

    boolean equals(Object obj);

    default Comparator<T> reversed() {

        return Collections.reverseOrder(this);

    }

    default Comparator<T> thenComparing(Comparator<? super T> other) {

        ...

    }

    ...

}

虽然Comparator接口有很多方法,但只有一个抽象方法int compare(T o1, T o2),其他的方法都是default方法或static方法。另外注意到boolean equals(Object obj)是Object定义的方法,不算在接口方法内。因此,Comparator也是一个Functional Interface。

 

参考链接:

https://www.runoob.com/java/java8-lambda-expressions.html

https://www.liaoxuefeng.com/wiki/1252599548343744/1305158055100449

 

 

posted @ 2021-11-01 05:46  吴建明wujianming  阅读(103)  评论(0编辑  收藏  举报