函数式接口

只包含一个抽象方法的接口,称为函数式接口。

你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。

我们可以在一个接口上使用 @FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

java.util.function包下定义了Java 8 的丰富的函数式接口

image-20200923165329350

我们查看Comparator接口源码。发现了一些矛盾的地方。

image-20200923165757180

我们发现了该接口中不止包含了一个方法,还存在大量的defaultstatic方法。这和上文的定义是矛盾的!!

在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的。由于这些修饰符都是默认的,JDK8及以后,允许我们在接口中定义static方法和default方法。

JDK8接口中的静态方法和默认方法,都不算是抽象方法。。接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。

image-20200923170228916

所以该接口的确是一个函数式接口!!

Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP) 编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不 得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还 可以支持OOF(面向函数编程)

在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的 编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在 Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的 对象类型——函数式接口。

简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是 Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口 的实例,那么该对象就可以用Lambda表达式来表示。

所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

@FunctionalInterface

image-20200923170602530

  1. 该注解只能标记在”有且仅有一个抽象方法”的接口上。

  2. JDK8接口中的静态方法和默认方法,都不算是抽象方法。

  3. 接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。

  4. 该注解不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

自定义函数式接口

通过对函数式接口概念的认知,我们现在来自定义一个MyFunctionInterface 函数式接口测试。

只包含一个抽象方法

image-20200923171412859

1
2
3
4
5
6
//自定义函数式接口
public interface MyFunctionInterface<T> {

//抽象方法
T test(T t);
}

测试类TestFunction

image-20200923171514395

1
2
3
4
5
6
7
8
9
10
public class TestFunction {

public static void main(String[] args) {
MyFunctionInterface<String> function = (str) -> {
return str;
};

System.out.println(function.test("FunctionInterface Test!"));
}
}

包含一个覆盖Object方法

覆盖Object的 hashCode方法

image-20200923171729178

再次运行测试类,成功!说明它是一个函数式接口

image-20200923171514395

1
2
3
4
5
6
7
8
9
10
//自定义函数式接口
public interface MyFunctionInterface<T> {

//抽象方法
T test(T t);

//覆盖Object方法
int hashCode();

}

包含一个default方法

image-20200923172018308

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//自定义函数式接口
public interface MyFunctionInterface<T> {

//抽象方法
T test(T t);

//覆盖Object方法
int hashCode();

//默认方法
public default void defaultMethod(){

}

}

再次运行测试类,成功!说明它default不是抽象方法,该接口是一个函数式接口

image-20200923171514395

包含一个static方法

image-20200923185337449

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface MyFunctionInterface<T> {

//抽象方法
T test(T t);

//覆盖Object方法
int hashCode();

//默认方法
public default void defaultMethod(){

}

//静态方法
public static void staticMethod(){

}
}

再次运行测试类,成功!说明它static不是抽象方法,该接口是一个函数式接口

image-20200923171514395

标注@FunctionInterface注解

通过前面的测试,我们可以明确知道,只要该接口只含有一个抽象方法,它就是一个函数式接口。而不是一定要标注@FunctionInterface注解,这两者之间没有绝对的联系。

该注解不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

image-20200923185744315

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//自定义函数式接口
@FunctionalInterface
public interface MyFunctionInterface<T> {

//抽象方法
T test(T t);

//覆盖Object方法
int hashCode();

//默认方法
public default void defaultMethod(){

}

//静态方法
public static void staticMethod(){

}
}

四大核心函数式接口

函数式接口 参数类型 返回类型 用途
Consumer<T>消费型 T void 对类型为T的对象应用操作,包含方法:void accept(T t)
Supplier<T>供给型 T 对类型为T的对象,包含方法:T get()
Function<T,R>函数型 T R 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate<T>断定型 T boolean 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法boolean test(T t)

Consumer消费型

image-20200923193957077

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void test1(){

happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble);
}
});

System.out.println("********************");

happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money));
}

public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}

image-20200923192400600

Supplier供给型

image-20200923193152964

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void test2() {

System.out.println(get(new Supplier<String>() {
@Override
public String get() {
return "犯罪人:张三";
}
}));

System.out.println(get(() -> {
return "法外狂徒三哥!";
}));
}

public String get(Supplier<String> supplier) {
return supplier.get();
}

image-20200923193220508

Function函数型接口

image-20200923193912896

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void test3() {
System.out.println(getLength("test", new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
}));

System.out.println(getLength("lambda", str -> str.length()));
}

public int getLength(String s, Function<String, Integer> function) {
return function.apply(s);
}

image-20200923192317870

Predicate断定型接口

image-20200923194231733

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
@Test
public void test4() {
List<String> list = Arrays.asList("北京", "南京", "天津", "东京", "西京", "普京");

List<String> filterStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("津");
}
});

System.out.println(filterStrs);


List<String> filterStrs1 = filterString(list, s -> s.contains("京"));
System.out.println(filterStrs1);
}

//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre) {

ArrayList<String> filterList = new ArrayList<>();

for (String s : list) {
if (pre.test(s)) {
filterList.add(s);
}
}

return filterList;

}

image-20200923194305931

其他接口

函数式接口 参数类型 返回类型 用途
BiFunction<T, U, R> T, U R 对类型为 T, U 参数应用操作,返回 R 类型的结 果。包含方法为: R apply(T t, U u);
UnaryOperator (Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的 结果。包含方法为:T apply(T t);
BinaryOperator (BiFunction 子接口) T, T T 对类型为T的对象进行二元运算,并返回T类型的 结果。包含方法为: T apply(T t1, T t2);
BiConsumer<T, U> T, U void 对类型为T, U 参数应用操作。 包含方法为: void accept(T t, U u)
BiPredicate<T,U> T,U boolean 包含方法为: boolean test(T t,U u)
ToIntFunction< T > ToLongFunction< T > ToDoubleFunction< T > T int long double 分别计算int、long、double值的函数
IntFunction< R > LongFunction< R > DoubleFunction< R > int long double R 参数分别为int、long、double 类型的函数

image-20200923194851914