函数式编程与lambda表达式(基础篇)

Java 8 早已经在2014 年 3月 18日发布,毫无疑问 Java 8 对 Java 来说绝对算得上是一次重大版本更新,它包含了十多项语言、库、工具、JVM 等方面的新特性。
比如提供了语言级的匿名函数,也就是被官方称为 lambda 的表达式语法(外界也称为闭包,lambda 的引入也让流式操作成为可能,减少了代码编写的复杂性),比如函数式接口,方法引用,重复注解。再比如 Optional 预防空指针,Stearm 流式操作,LocalDateTime 时间操作等。

为什么使用lambda表达式?

  • 减少代码编写的复杂性
  • 让流式操作称为可能Stream APIStream\ API
@Test
public void createLambda() throws InterruptedException {
    // 使用 Lambda 之前
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("JDK8 之前的线程创建");
        }
    };
    new Thread(runnable).start();
    // 使用 lambda 之后
    Runnable runnable1Jdk8 = () -> System.out.println("JDK8 之后的线程创建");
    new Thread(runnable1Jdk8).start();
    // 更加紧凑的方式
    new Thread(() -> System.out.println("JDK8 之后的线程创建")).start();
}

可以发现 Java 8 中的lambdalambda碰到了函数接口RunnableRunnable,自动推断了要运行的runrun方法,不仅省去了runrun方法的编写,也代码变得更加紧凑



lambda表达式的使用

1. (params) -> expression
2. (params) -> {statements;}
  • >-> : 分割lambdalambda参数和处理语句
  • 左边:lambdalambda形参列表:其实就是接口中抽象方法的形参列表
  • 右边:lambdalambda体:重写的抽象方法体

无参数无返回值

public void test1() {
    Runnable r1 = new Runnable(){
        @Override
        public void run() {
            System.out.println("Rick");
        }
    };
    r1.run();
    System.out.println("---------------");
    Runnable r2 = () -> System.out.println("Rick");
    r2.run();
}

lambda需要一个参数,但是没有返回值

public void test2() {
    Consumer<String> con1 = new Consumer<String>(){

        @Override
        public void accept(String t) {
            System.out.println(t);
        }
        
    };
    con1.accept("Rick");
    System.out.println("-----------------------");
    Consumer<String> con2 = (String t) -> {
        System.out.println(t);
    };
    con2.accept("Rick");
}

数据类型可以省略,可由编译器推断得出

public void test3() {
    Consumer<String> con3 = (t) -> {
        System.out.println(t);
    };
    con3.accept("Rick");
}

lamdba只需要一个参数时,小括号可省略

public void test4() {
    Consumer<String> con4 = t -> {
        System.out.println(t);
    };
    con4.accept("Rick");
}

lambda需要不止一个参数,且有返回值

public void test5() {   
    Comparator<Integer> com = new Comparator<Integer>(){
        @Override
        public int compare(Integer o1, Integer o2) {
            return Integer.compare(o1, o2);
        }
    };
    System.out.println(com.compare(12, 14));
    System.out.println("---------------");
    Comparator<Integer> com5 = (o1, o2) -> {
        return Integer.compare(o1, o2);
    };
    System.out.println(com5.compare(12, 14));
}

当lambda体只有一条语句时,return与大括号若有,都可省略

public void test6() {
    Comparator<Integer> com6 = (o1, o2) -> Integer.compare(o1, o2);
    System.out.println(com6.compare(12, 14));
}


函数式接口

Java 中的函数接口在使用时,可以隐式的转换成lambdalambda表达式,在Java 8中已经有很多接口已经声明为函数接口,如Runnable,Callable,ComparatorRunnable, Callable, Comparator等。
我们来看RunnableRunnable接口:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

那么什么样子的接口才是函数接口呢?有一个很简单的定义

  • 只有一个抽象函数的接口,函数接口使用注解 @FunctionalInterface 进行声明(注解声明不是必须的,如果没有注解,也是只有一个抽象函数,依旧会被认为是函数接口,并且该接口是一个编译级别的注解,用于检测函数式接口是否有错而已)
  • 多一个或者少一个抽象函数都不能定义为函数接口。但是,函数式接口里是可以包含默认方法、静态方法和ObjectObject里的publicpublic方法,因为这些方法就不是抽象方法了,其有默认实现,所以是符合函数式接口的定义
  • 如果使用了函数接口注解又不止一个抽象函数,那么编译器会拒绝编译。函数接口在使用时候可以隐式的转换成lambdalambda表达式

消费性接口Consumer:接收一个参数T,没有返回值

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    ...
}

测试

@Test
public void test1() {
    StringBuilder sb = new StringBuilder("Hello ");
    Consumer<StringBuilder> consumer = (str) -> str.append("World!");
    consumer.accept(sb);
    System.out.println(sb);     // Hello World!
}

供给型接口Supplier:不接受任何参数但有返回值

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

测试

@Test
public void test2() {
    Supplier<String> supplier = () -> "Hello World!";
    System.out.println(supplier.get());     // Hello World!
}

断言性接口Predicate:判断方法的参数是否符合断言

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    ...
}

测试过滤字符串

@Test
public void test3() {
    List<String> list = Arrays.asList("ABC", "ADR", "CDK", "ABU");
    System.out.println(filterString(list, new Predicate<String>(){
        @Override
        public boolean test(String t) {
            return t.contains("A");
        }
    }));
    System.out.println("------------------");
    // (s) -> s.contains("A")) :筛选出含有A
    System.out.println(filterString(list, (s) -> s.contains("A")));
}

/**
 * 过滤列表中的字符串
 * @param list
 * @param pr
 * @return
 */
public List<String> filterString (List<String> list, Predicate<String> pr) {
    List<String> res = new ArrayList<>();
    for (String str : list) {
        if (pr.test(str)) 
            res.add(str);
    }
    return res;
}

功能性接口Function:接收一个功能参数T,并返回一个功能结果R

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
    ...
}

测试

@Test
public void test4() {
    Function<String, String> function = (s1) -> s1 + "World!";
    System.out.println(function.apply("Hello "));
}

赞赏