Java|Lambda、函数式接口使用

目录

Lambda 是 JDK8 的新特性,Lambda 让 Java 支持了函数式的编程,如 JS 那样可以在参数中使用函数(JS 中函数是对象)。

这些特性是 Java8 的一些特性,使用这些特性能够简化我们的代码,让代码更清晰。

Lambda 使用示例

在支持回调的方法中使用,典型的 Runnable 接口的 run(),Comparator 的 compare()。

    // 使用Lambda执行一个线程的run方法
    new Thread(() -> {
        try {
            while (true) {
                System.out.println("hello elltor!");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

    // 自定义compare实现
    Arrays.sort(strs,(str1,str2)->{
        // 根据首字母ascii差值判断大小
        char c = str1.charAt(0);
        char c2 = str2.charAt(0);
        return c-c2;
    });

    for (String str : strs) {
        System.out.println(str);
    }

    // 自定义compare实现2
    Comparator<String> comparator = (str1,str2)->{
        char c = str1.charAt(0);
        char c2 = str2.charAt(0);
        return c-c2;
    };
    // 使用
    Arrays.sort(strs,comparator);

Lambda 语法与注意事项

首先,如果要使用 lambda,就要实现函数式接口,如 Runnable 的 run 方法,稍后讲解函数式接口。

Lambda 语法。

Lambda 的标志是 “->”, 前面接收参数的括号和后面的方法题在特殊的情况下都可以省略。

// 基本语法
() ->{}

// 省略括号,当且仅当只有一个参数时才允许这么做
ActionListener listener = e -> {
    System.out.println(this.aa);
    System.out.println("监听得到事件");
};

// 省略方法体,当且仅当方法体只有只有一个return+返回值时才允许这么做
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.toString());
list.removeIf(val -> val <= 2); // 即省略了括号也省略了方法体
System.out.println(list.toString());

使用 Lambda 的注意事项:

  • Lambda 方法体中不具有 this,它包含的 this 是包含 Lambda 的方法的 this
  • Lambda 方法体中引用的变量不能改变,不能在方法体外边改变或者里面改变,这是 Lambda 的语法规定。从语言层面来讲,如果可以改变那将带来线程安全性问题。
  • Lambda 函数回调有固定的类型,这些类型根据函数式接口确定,Runnable 的 run 接收的是一个无参的 Lambda,而 Predicate 接收的是一个参数 Lambda

函数式接口

实现 Lambda 依赖的是函数式接口,函数式接口规定了传入什么样格式的 Lambda(有无参数或返回值,参数个数等)。函数式接口(Function Interface)是 java.util.function 包下的一组抽象接口。

image.png

接口支持 Lambda 的原理——一方法参数使用了函数接口,二方法调用函数式接口的方法。

先看 ArrayList 的 removeIf 的源码。

    // ArrayList的removeIf的源码
    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

// Predicate 核心源码
@FunctionalInterface
public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     */
    boolean test(T t);
}

方法引用

方法引用的语法:

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod

示例

List<Integer> list = Arrays.asList(1,2,3,4,5);
list.forEach(System.out::println);
String[] strings = new String[]{"c","b","a"};

Arrays.sort(strings,String::compareToIgnoreCase);
System.out.println(Arrays.toString(strings));

方法的引用是传递一个方法的引用过去,它等价于 Lambda 的 x->System.out.println(x).

当被传入的方法有重载时,编译器会根据上下文推断应该调用的方法是什么类型,如 Math::max 方法有两个,传入 double 和传入 int 的,选择哪一个取决于 Math::max 函数式接口的具体参数, 关键在多态运行时的匹配。

public class RefTest {
    public static void main(String[] args) {
        RefTest.printMax(2,3,Math::max);
    }

    static void printMax(int a, int b, BiFunction<Integer,Integer, Integer> accept){
	// apply将调用Math.max()方法,并返回比较结果
        System.out.println("max = " + accept.apply(a, b));
    }
}
// output
max = 3

构造器引用

构造器引用示例

List<String> names = Arrays.asList("zhangsan","lisi","wanger");
names.stream().map(Person::new).collect(Collectors.toList());

常用函数式接口

接口 参数 返回类型 抽象方法 描述 其他方法
Runable void run 作为无参或返回值的动作执行
Consumer
BiComsumer<T,U>
Function<T,R>
BiFunction<T,U,R>
UnaryOperator<T>
BinaryOperator<T>
Predicate<T>
BiPredicate<T,U>