Java Lambda 表达式使用

Java8 引入了 Lambda 表达式,使用 Lambda 表达式可以让代码更加简洁。Lambda 表达式其实也就是一个匿名函数,我们可以用它去代替匿名函数,我们先来看一个例子

1、Lambda 表达式语法

我们用接口 Runnable 举个例子

public static void main(String[] args) {
        
    // 匿名函数
    new Thread(new Runnable() {
        public void run() {
            System.out.println("anonymous function");
        }
    }).start();
        
    // lambda 表达式
    new Thread(() -> System.out.println("lambda")).start();
}

从上的例子可以看出我们使用 () -> {} 的代码块代替了整个匿名函数

Lambda 表达式的语法格式如下
( parameters ) -> expression
( parameters ) -> {statements;}

  • 可以不需要声明参数类型,编译器可以统一识别参数值。
  • 一个参数时可以不定义圆括号,但多个参数需要定义圆括号。
  • 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值(如果接口方法返回类型为 void 则不用)。

Lambda 表达式的简单例子

// 无参数,返回值为 10
() -> 10  
  
// 单参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 多个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 声明参数类型,返回他们的和  
(int x, int y) -> return x + y  
  
// 声明参数类型,并在控制台打印,不返回任何值(接口方法返回类型为 void)  
(String s) -> System.out.print(s)

2、Lambda 行为参数化和变量作用域

我们可以将lambda表达式作为参数传递给方法,也就是说允许把函数作为一个方法的参数。

创建一个 Calculator 的函数接口。 在 Calculator 中有一个称为 calculate 的方法,它接受两个 int 参数并返回一个 int 值。
在 engine 方法中,它接受函数接口 Calculator 作为参数。在主方法中,用不同的lambda表达式调用 engine 方法四次

public class LambdaTest {

    public static void main(String[] argv) {
        engine((x, y) -> x + y);
        engine((x, y) -> x * y);
        engine((x, y) -> x / y);
        engine((x, y) -> x % y);
    }

    private static void engine(Calculator calculator) {
        int x = 2, y = 4;
        int result = calculator.calculate(x, y);
        System.out.println(result);
    }
}

@FunctionalInterface
interface Calculator {
    int calculate(int x, int y);
}

lambda 表达式不定义自己的范围。
如果我们在 lambda 中使用关键字 thissuperthis 代表着 lambda 表达式所被包含的类

变量作用域

lambda 表达式与其外部方法具有相同的范围。 lambda表达式不会创建自己的作用域。

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

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());  //编译会出错 

3、函数式接口

函数式接口只是具有一个方法的接口,用作 lambda 表达式的类型。
通常会使用 @FunctionalInterface 注解来标注了函数式接口

在 java8 中为我们定义了很多常用的函数式接口,它们都放在java.util.function包下面,一般有以下常用的四大核心接口

函数式接口 参数类型 返回类型 描述
Consumer(消费型接口) T void 对类型为T的对象应用操作。void accept(T t)
Supplier(供给型接口) T 返回类型为T的对象。 T get();
Function<T, R>(函数型接口) T R 对类型为T的对象应用操作并返回R类型的对象。R apply(T t);
Predicate(断言型接口) T boolean 确定类型为T的对象是否满足约束。boolean test(T t);

4、方法引用

方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 ::

1)构造函数引用

使用构造函数引用的语法是 ClassName::new

Function<String,String> func1  = str ->  new String(str);

Function<String,String> func2  = String::new;

2) 静态方法引用

方法引用的一般语法是 Qualifier::MethodName

Function<Integer, String> func1  = x -> Integer.toBinaryString(x);

Function<Integer, String> func2  = Integer::toBinaryString;

3) 实例方法引用

Supplier<Integer> supplier  = () ->  "www.w3cschool.cn".length(); 

Supplier<Integer> supplier1  = "www.w3cschool.cn"::length; 

4) 通用方法引用

Function<String[],List<String>> asList = Arrays::<String>asList;
    
System.out.println(asList.apply(new String[]{"a","b","c"}));

猜你喜欢

转载自blog.csdn.net/e_zxq/article/details/86496301