Functional 为 lambda 方法提供一个接口

函数式接口

Functional 的定义其实很简单:任何接口,如果只包含 唯一一个抽象方法,那么它就是一个 Functional Interface ,
函数式接口?这注解名字好。
为了让编译器帮助我们确保一个接口满足 Functional Interface 的要求(也就是说有且仅有一个抽象方法),Java8提供了@FunctionalInterface注解。

先学习函数式接,才能理解学习 Lambda 表达式。

有什么用

白话:用来为函数式编程提供接口。
就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。

简单示例

写一个最简单的例子做为入门参考,看不懂不要紧,这个只是一个例子。

声明一个无参的接口

声明一个无参函数式接口,为 lambda 提供一个接口。
在接口上添加@Functional那么这个接口就是一个函数式接口

1
2
3
4
@FunctionalInterface
public interfaceMyFunctional {
public abstract void run ();
}

实现接口

自己实现一个Functional接口,只需要 ()-> System.out.println("test") 进行实现。()->System.out.println("test") 就是一个lambda实现。
这里主要关注Functional接口,不需要关注lambda

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
//实现 lambda
MyFunctional myFunctional = () -> System.out.println("test");
//调用 Functional 接口来执行 lambda 实现
System.out.println(myFunctional.run());
}
}

使用规则

首先它是一个接口。然后这个接口里面只能有一个抽象方法。
这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。

规则

  1. 函数式接口里允许定义默认方法,因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的
  2. 函数式接口里允许定义静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的
  3. 函数式接口里允许定义Java.lang.Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自java.lang.Object里对这些抽象方法的实现;
  4. 函数式接口里允许子接口继承多个父接口,但每个父接口中都只能存在一个抽象方,且必须的相同的抽象方法。

jdk 中的 lambda 实现示例

使用 Arrays 对数据进行排序。
Arrays 的 sort 方法,可以使用 lambda 的方法,这个方法是怎么实现的呢。
老套路:

  1. 定义 Functional 接口
  2. Functional 做为形参
  3. 有一个地方实现了这个Functional接口
  4. 调用
1
2
3
4
5
6
// 使用 lambda expression 排序 players
Comparator<String> sortByName = (s1, s2) -> (s1.compareTo(s2));
Arrays.sort(players, sortByName);

//等价形式
Arrays.sort(players, (s1, s2)-> (s1.compareTo(s2)));

看看 sort 的具体实现,sort 是对 Comparator 接口的具体实现。

1
2
3
4
5
6
7
8
9
10
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}

Comparator 接口

1
2
3
4
@FunctionalInterface
public interface Comparator<T> {
...
}

默认方法 和 静态方法

默认方法是可以直接被 类名.方法名使用的。
lambda 的默认实现方法,可以直接写在接口当中,然后具体使用的时候引用即可。
封装的时候也可以直接引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.Objects;
import java.util.function.Predicate;

@FunctionalInterface
public interface MyFunctional<T> {
//只允许存在一个 public abstract 方法
boolean test(T t);
// 这个方法就是对上面 boolean test(T t); 的一个默认实现
static <T> boolean getBoolean (T b) {
System.out.println(b.toString());
return true;
}
//由于使用了范形,入参就变得灵活
default String testPrintString (String message) {
System.out.println(message); return message;
}
//使用范形方法
default <T> boolean testPrint (T message) {
System.out.println(message); return true;
}
//允许存在默认方法
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}

default Predicate<T> negate() {
return (t) -> !test(t);
}

default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
} //允许存在静态方法
static <T> Predicate<T> isEqual(Object targetRef) {
System.out.println("使用静态方法");
return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void testStaticMethod() {
//直接使用默认实现
static <T > boolean getBoolean (T b)
MyFunctional myFunctional = MyFunctional::getBoolean;
myFunctional.test(new Date());
myFunctional.testPrintString("test....");
//使用具体类型,默认方法
myFunctional.testPrint(new Date());
//使用范形默认方法
// 直接传入默认实现即可
testDefault(MyFunctional::getBoolean);
//使用静态方法
MyFunctional.isEqual(new Date());
}

//封装 lambda 表达式
public static String testDefault(MyFunctional myFunctional) {
return myFunctional.testPrintString("just test lambda");
}