-
函数式接口-Lambda表达式的主战场
函数式接口
有且仅有一个抽象方法的接口被称为函数式接口。
函数式接口可以使用注解@FunctionalInterface
进行校验。如同常用的@Override
注解一样,该注解不会对接口/方法产生实质性的修改,只是作校验用。
函数式接口仅是一个接口,它的具体逻辑实现根据调用时输入的Lambda表达式来确定。
另外,函数式接口只是一种被命名的书写格式,它的学习类似于我们学习普通接口,学习for循环一样,它与学习功能型框架的约定使用方式不同。也许在示范使用的过程中,你会想到其他的实现方式,但是功能的实现是因人而异的,如何实现的决定权在于使用者。换句话说,“没吃过猪肉,也要见过猪跑”。
由于函数式接口与Lambda紧密而不可分,我们可以将函数式接口的学习作为进一步掌握Lambda表达式的使用的试验场。
Java为我们提供了许多函数式接口,其中的许多我们之前已经使用过。还有一些默认的接口我更愿意称其为Java提供的函数式接口模板,供人学习和使用。下面通过案列来介绍函数式接口的使用及常见的函数式接口
一、函数式接口的使用
1.作为方法参数
在创建线程并使用的过程中,我们会使用这样的方式
new Thread(
() -> System.out.println(Thread.currentThread().getName() + " run...")
).start();
其中,Lambda表达式代表的匿名内部类就是一个函数式接口Runnable
。它同时使用了@FunctionalInterface
注解进行修饰
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
2.作为方法返回值
对于Collection的字类排序,我们常使用Collections.sort()
方法
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
其中,第二个参数Comparator
排序器就是一个函数式接口。我们也会使用Lambda表达式来书写排序方式
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("ccc");
list.add("c");
list.add("bb");
list.add("dddd");
System.out.println(list); // 排序前
Collections.sort(list);
System.out.println(list); // 使用默认排序
Collections.sort(list, getComparator());
System.out.println(list); // 使用长度排序
}
private static Comparator<String> getComparator(){
//匿名内部类
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.length()-o2.length();
// }
// };
//Lambda
return ((o1, o2) -> o1.length()-o2.length());
//方法引用
// return (Comparator.comparingInt(String::length));
}
当然,我们也可以简略的写为
Collections.sort(list, return ((o1, o2) -> o1.length()-o2.length()));
二、其他常见的函数式接口
Java总结了人们可能用到的函数式接口,提供了一些示例模板,我们也可以直接使用
1.Supplier接口
Supplier<T>
供应商/生产型接口。T代表要供应的结果,仅有一个T get()
方法。
简单来说,调用该接口的目的就是获得某个类型的对象。例如调用Supplier<String>
接口就是为了获得String类型的数据。
下面用代码做示例
/**
* Supplier 生产型接口,返回泛型定义的类型
*/
public class SupplierDemo01 {
public static void main(String[] args) {
//使用Lambda表达式作为接口的实现,返回一个字符串结果
System.out.println(getString(() -> "asd"));
System.out.println(getInteger(() -> 123));
}
//Supplier接口作为方法的参数,接口的实现由调用者实现
private static String getString(Supplier<String> sup){
return sup.get();
}
private static Integer getInteger(Supplier<Integer> sup){
return sup.get();
}
}
2.Cunsumer接口
Cunsumer<T>
消费者/消费型接口。T代表要用于消费的类型。
简单来说就是,获得参数后,执行某个操作, 不返回结果。这里介绍两个方法
void accept(T t)
:接收并执行对应操作方法
Consumer<T> andThen(Consumer<? super T> after)
:消费后执行下一个消费操作方法
在这里,你可能会存在疑问,它有不只一个方法呀,怎么会是函数式接口呢?其实,andThen()
方法并不是抽象方法,它是有default实现的:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
注意:JDK1.8中,允许接口存在默认实现的方法体
下面使用代码来介绍该接口
public static void main(String[] args) {
operator("bilibili", s -> System.out.println(s));
operator("bilibili", System.out::println); // 方法引用方式
System.out.println("。。。。。。");
operatorTwice("bilibili", System.out::println, s->System.out.println(new StringBuilder(s).reverse().toString()));
}
//消费一次数据
private static void operator(String name, Consumer<String> con){
con.accept(name);
}
//消费多次数据
private static void operatorTwice(String name, Consumer<String> con1, Consumer<String> con2){
con1.andThen(con2).accept(name);
//等同于
// con1.accept(name);
// con2.accept(name);
}
得到输出结果
bilibili
bilibili
。。。。。。
bilibili
ilibilib
3.Predicate接口
Predicate<T>
校验型接口。对提供参数进行断言判断。具体的校验逻辑由调用者的Lambda表达式提供
boolean test(T t)
:测试方法
Predicate<T> negate()
:非逻辑方法
Predicate<T> and(Predicate other)
:与逻辑方法
Predicate<T> or(Predicate other)
:或逻辑方法
上代码
public static void main(String[] args) {
System.out.println(checkString("Hello World!", s -> s.length()>5));
System.out.println(negateCheckString("Hello World!", s -> s.length()>5));
System.out.println(andCheckString("Hello",
s -> s.length()>8,
s -> s.length()<20
));
}
private static boolean checkString(String str, Predicate<String> pre){
return pre.test(str);
}
//非逻辑
private static boolean negateCheckString(String str, Predicate<String> pre){
return pre.negate().test(str);
//等同于
// return !pre.test(str);
}
//与逻辑
private static boolean andCheckString(String str, Predicate<String> pre1, Predicate<String> pre2){
return pre1.and(pre2).test(str);
//等同于
// return pre1.test(str) && pre2.test(str);
}
输出结果
true
false
false
4.Function接口
Function<T,R>
功能型接口。根据T类型的参数返回R类型的结果。它如同我们普通的方法,接收参数,处理数据,返回结果。
R apply(T t)
:处理数据方法
Function<T,V> andThen(Function after)
:处理后执行下一个消费操作方法
上代码
public static void main(String[] args) {
convert("123",s -> Integer.parseInt(s));
convert(123, i -> String.valueOf(i+100));
convert("365",
Integer::parseInt,
i -> String.valueOf(i+100));
}
//定义一个方法,将字符串转换为int并输出
private static void convert(String s, Function<String, Integer> fun){
Integer apply = fun.apply(s);
System.out.println(apply);
}
//定义一个方法,在int后添加一些数据并输出
private static void convert(Integer i, Function<Integer,String> fun){
System.out.println(fun.apply(i));
}
//定义一个方法,将字符串转换为int,并在int后添加一些数据,并输出
private static void convert(String s, Function<String,Integer> fun1, Function<Integer,String> fun2){
// Integer integer = fun1.apply(s);
// String str = fun2.apply(integer);
// System.out.println(str);
//等同于
System.out.println(fun1.andThen(fun2).apply(s));
}
运行结果
123
223
465
三、总结
- 有且只有一个抽象方法的接口被称为函数式接口
-
函数式接口可以使用
@FunctionalInterface
注解进行校验 - 函数式接口内抽象方法的实现常用Lambda表达式书写