函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。
而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
函数式编程最早是数学家阿隆佐·邱奇研究的一套函数变换逻辑,又称Lambda Calculus(λ-Calculus),所以也经常把函数式编程称为Lambda计算。
Java平台从Java 8开始,支持函数式编程。
这部分整理的导图:链接
1. Lambda基础回顾:java的方法
**概念:FunctionalInterface **
把只定义了单方法的接口称之为FunctionalInterface,用注解@FunctionalInterface标记。
Comparator
Runnable
Callable
例如,Callable接口:
@FunctionalInterface public interface Callable<V> { V call() throws Exception; } Lambda表达式在使用单方法接口的时候,以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)); } }观察Lambda表达式的写法,它只需要写出方法定义:
(s1, s2) -> { return s1.compareTo(s2); }其中,
参数是(s1, s2),参数类型可以省略,因为编译器可以自动推断出String类型。
-> { ... }表示方法体,所有代码写在内部即可。Lambda表达式没有class定义,因此写法非常简洁。
如果只有一行return xxx的代码,完全可以用更简单的写法:
Arrays.sort(array, (s1, s2) -> s1.compareTo(s2));返回值的类型也是由编译器自动推断的,这里推断出的返回值是int,因此,只要返回int,编译器就不会报错。
2. 方法引用在上面介绍了 Lambda的表达式,但是,还可以更进一步,不写表达式,直接传入方法引用
2.1 引用静态方法 import java.util.Arrays; public class Main { public static void main(String[] args) { String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" }; Arrays.sort(array, Main::cmp); System.out.println(String.join(", ", array)); } static int cmp(String s1, String s2) { return s1.compareTo(s2); } }在Arrays.sort()中直接传入了静态方法cmp的引用,用Main::cmp表示。
因此,所谓方法引用,是指如果某个方法签名和接口恰好一致,就可以直接传入方法引用。
注意:在这里,方法签名只看参数类型和返回类型,不看方法名称,也不看类的继承关系。
因为Comparator接口定义的方法是int compare(String, String),和静态方法int cmp(String, String)相比,除了方法名外,方法参数一致,返回类型相同,因此,我们说两者的方法签名一致,可以直接把方法名作为Lambda表达式传入:
Arrays.sort(array, Main::cmp); 2.2 引用实例方法 import java.util.Arrays; public class Main { public static void main(String[] args) { String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" }; Arrays.sort(array, String::compareTo); System.out.println(String.join(", ", array)); } }观察String.compareTo()的方法定义:
public final class String { public int compareTo(String o) { ... } }这个方法的签名只有一个参数,为什么和int Comparator.compare(String, String)能匹配呢?
因为实例方法有一个隐含的this参数,String类的compareTo()方法在实际调用的时候,第一个隐含参数总是传入this,相当于静态方法:
public static int compareTo(this, String o);所以,String.compareTo()方法也可作为方法引用传入。
2.3 引用构造方法例子:如果要把一个List转换为List
// 引用构造方法 import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { List<String> names = List.of("Bob", "Alice", "Tim"); List<Person> persons = names .stream() .map(Person::new) .collect(Collectors.toList()); System.out.println(persons); } } class Person { String name; public Person(String name) { this.name = name; } public String toString() { return "Person:" + this.name; } }这里的map()需要传入的FunctionalInterface的定义是:
@FunctionalInterface public interface Function<T, R> { R apply(T t); }