0%

Java基础—函数式接口

这一部分主要记录了Java函数式接口的使用方法以及常用的函数式接口,函数式接口(Functional Interface)是Java 8对一类特殊类型的接口的称呼。 这类接口只定义了唯一的抽象方法的接口(除了隐含的Object对象的公共方法)。函数是接口是因为Lambda表达式必须要依赖于上下文存在,故此类接口是Lambda表达式(包括方法引用)的一种实现方式。

文章结构分为「函数式接口的概述、函数是接口的使用方式、常用的函数式接口」三个部分。

函数式接口的概述

概念

  • 有且仅有一个抽象方法的接口,即可以使用Lambda表达式的接口
  • 函数式接口可以用作【参数传递】&【局部变量】

函数式接口标记
@FunctionalInterface(建议加上)

放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败

函数式接口的使用方式

函数式接口作为方法的参数(以Runnable()为例)

要求
定义一个类(RunnableDemo),在类中提供两个方法:

一个方法是:startThread(Runnable r) 方法参数Runnable是一个函数式接口

一个方法是:主方法,在主方法中调用startThread方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public static void main(String[] args) {
//在主方法中调用startThread方法
//匿名内部类的方式
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程 - 匿名内部类的方式");
}
});
//Lambda表达式的方式
startThread(() -> System.out.println(Thread.currentThread().getName() + "线程 - Lambda表达式的方式"));
}

private static void startThread(Runnable r){
// Thread t = new Thread(r);
// t.start();
new Thread(r).start();
}
}

函数式接口作为方法的返回值(以Comparator() 为例)

要求

定义一个类(ComparatorDemo),在类中提供两个方法:

一个方法是:Comparator getComparator() 方法返回值Comparator是一个函数式接口

一个方法是主方法,在主方法中调用getComparator方法

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
public class ComparatorDemo {
public static void main(String[] args) {
//构造使用场景
ArrayList<String> array = new ArrayList<>();
array.add("abcdefghijklmn");
array.add("199");
array.add("1234567890");

System.out.println("排序前" + array);

Collections.sort(array, getComparator());

System.out.println("排序后" + array);
}

private static Comparator<String> getComparator() {

//匿名内部类实现
return new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
};

//Lambda表达式方式
return (s1, s2) -> s1.length() - s2.length(); //函数式接口作为方法的返回值,使用Lambda接口
}

}

常用的函数式接口

生产型接口——Supplier

作用

如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。

方法

方法名 说明
T get() 按照某种实现逻辑(由Lambda表达式实现)返回一个数据

示例

案例需求
定义一个类(SupplierTest),在类中提供两个方法

​ 一个方法是:int getMax(Supplier sup) 用于返回一个int数组中的最大值

​ 一个方法是主方法,在主方法中调用getMax方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SupplierTest {
public static void main(String[] args) {
int[] arr = {1,6,3,4};
int maxValue = getMax(() -> {
int max = arr[0];
for(int i = 1; i < arr.length; i++){
max = Math.max(max, arr[i]);
}

return max;
});

System.out.println(maxValue);
}

//使用 SupPlier<T> 的 get() 方法返回一个最大值,具体实现由主类中的Lambda实现
private static int getMax(Supplier<Integer> sup){
return sup.get();
}
}

消费型接口——Consumer

作用

对传入参数执行或依次执行操作,它消费的数据的数据类型由泛型指定

方法

方法名 说明
void accept(T t) 对给定的参数执行此操作
default Consumer andThen(Consumer after) 返回一个组合的Consumer,依次执行此操作,然后执行after操作
采用链式编程

示例

案例需求
String[] strArray = {“林青霞,30”, “张曼玉,35”, “王祖贤,33”};

字符串数组中有多条信息,请按照格式:“姓名:XX,年龄:XX”的格式将信息打印出来

要求
把打印姓名的动作作为第一个Consumer接口的Lambda实例

把打印年龄的动作作为第二个Consumer接口的Lambda实例

将两个Consumer接口按照顺序组合到一起使用

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ConsumerTest {
public static void main(String[] args) {
String[] strArray = {"小林,30", "小张,35", "小王,33"};

//con1 与 con2 的操作在此处定义
printNameAge(strArray, s -> System.out.print(s.split(",")[0]), (s -> System.out.println(s.split(",")[1])));
}

public static void printNameAge(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
for(String s : strArray){

//对传入参数 s ,先进行 con1 的操作;再进行 con2 的操作。
//con1 与 con2 的操作由主类Lambda表达式定义
con1.andThen(con2).accept(s);
}
}
}

判断用接口——Predicate

作用

Predicate接口通常用于判断参数是否满足指定的条件

方法

方法名 说明
boolean test(T t) 对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
default Predicate negate() 返回一个逻辑的否定,对应逻辑非
default Predicate and(Predicate other) 返回一个组合判断,对应短路与
default Predicate or(Predicate other) 返回一个组合判断,对应短路或

示例

  • 练习描述

    • String[] strArray = {“林青霞,30”, “柳岩,34”, “张曼玉,35”, “貂蝉,31”, “王祖贤,33”};

    • 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合

    • 同时满足如下要求:

      姓名长度大于2;年龄大于33

  • 分析

    • 有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断
    • 必须同时满足两个条件,所以可以使用and方法连接两个判断条件
  • 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PredicateTest {
public static void main(String[] args) {
String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};
ArrayList<String> array = myFilter(strArray, s -> s.split(",")[0].length() > 2, s -> Integer.parseInt(s.split(",")[1]) > 33);
System.out.println(array);
}



private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2){
ArrayList<String> array = new ArrayList<>();

for(String s : strArray){
//这里是对 s 判断一下 pre2 和 pre1 的真假,然后进行与操作
//至于 pre1 和 pre2 的判断过程,由主流的Lambda表达式给出
if(pre1.and(pre2).test(s)){
array.add(s);
}
}
return array;
}
}

数据处理返回用接口——Function

作用

Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值。转换前的数据类型是 T ,转换后的数据类型是 R 。

方法

方法名 说明
R apply(T t) 将此函数应用于给定的参数
default Function andThen(Function after) 返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果

示例

  • 练习描述
    • String s = “林青霞,30”;
      请按照我指定的要求进行操作:
    • 1:将字符串截取得到数字年龄部分
      2:将上一步的年龄字符串转换成为int类型的数据
      3:将上一步的int数据加70,得到一个int结果,在控制台输出
    • 请通过Function接口来实现函数拼接
  • 代码实现
1
2
3
4
5
6
7
8
9
10
11
12
public class FunctionTest {
public static void main(String[] args) {
String s = "林青霞,30";
Integer converted = convert(s, s1 -> Integer.parseInt(s.split(",")[1]), s2 -> s2 + 70);
System.out.println(converted);
}

private static Integer convert(String s, Function<String, Integer> fun1, Function<Integer, Integer> fun2){
//返回处理结果,对 s 先进行 fun1 的运算,再进行 fun2 的运算
return fun1.andThen(fun2).apply(s);
}
}