函数式接口
什么是函数式接口
函数式接口(Functional Interface)就是有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 Lambda 表达式。
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
定义一个函数式接口
package demo1;
@FunctionalInterface
public interface MyFunInterface {
public abstract void show();
default void showInit(){}
}
如何理解函数式接口
Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即Java不但可以支持OOP还可以支持OOF(面向函数编程)
在函数式编程语言当中,函数被当做一等公民对待。
在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。 在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
JDK 1.8 之前已有的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口:
java.util.function
Java 内置四大核心函数式接口
JAVA8在java.util.function定义了大量的函数式接口,核心函数式接口主要有一下四个:
Supplier接口
Interface Supplier:包含了一个无参的方法
- T get():获取结果。
- 该方法无需要参数,它会按照某种逻辑(由Lambda表达式实现)返回一个结果
- Supplier接口也被称为生产接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用,但不传入参数。
声明如下:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
案例1
import java.util.function.Supplier;
/**
* 理解输出Supplier函数式接口
* 以返回一个字符串“123”为例
*
* @author Anna.
* @date 2024/4/3 12:37
*/
public class SupplierDemo3 {
public static void main(String[] args) {
// 通过实现Supplier接口调用get()方法实现
Supplier<String> stringSupplier = new Supplier<String>() {
@Override
public String get() {
return "123";
}
};
System.out.printf("实现Supplier接口的Supplier:%s%n", stringSupplier);
System.out.printf("通过实现Supplier接口调用get()方法实现:%s%n", stringSupplier.get());
// 通过函数式接口传入Lambda表达式的方式实现
String str = getStringBySupplier(() -> "123");
System.out.printf("通过函数式接口传入Lambda表达式的方式实现:%s%n", str);
}
/**
* 定义一个返回类型是String的Supplier接口函数调用方法
*
* @author Anna.
* @date 2024/4/3 12:39
*/
public static String getStringBySupplier(Supplier<String> supplier) {
System.out.printf("定义方法中的Supplier:%s%n", supplier);
return supplier.get();
}
}
函数式接口关心入参及返回值,执行过程可以看着将Lambda表达作为参数进行传递,具体实现有由Lambda表达式内部方法体完成。
通过上述结果我们可以看出,实现Supplier接口的Supplier是SupplierDemo3实例的一个内部类,而定义方法中的Supplier返回的却是一个Lambda。
案例2
package demo2;
import java.util.function.Supplier;
/**
* 使用Supplier<T>定义一个泛型方法,等到一个同类型的返回数据
*
* @author Anna.
* @date 2024/4/1 23:57
*/
public class SupplierDemo {
public static void main(String[] args) {
// 匿名内部类方法实现
String str = null;
str = get(new Supplier<String>() {
@Override
public String get() {
return "Hello".toUpperCase();
}
});
System.out.printf("匿名内部类方法实现:%s%n",str);
// Lambda表达式实现
str = get(() -> {return "Hello".toUpperCase();});
System.out.printf("Lambda表达式实现:%s%n",str);
// Lambda表达式简写实现
str = get(() -> "Hello".toUpperCase());
System.out.printf("Lambda表达式简写实现:%s%n",str);
// 方法引用实现
str = get("Hello"::toUpperCase);
System.out.printf("方法引用实现:%s%n",str);
}
/**
* 定义一个泛型方法,等到一个同类型的返回数据
*
* @param supplier
* @return T
* @author Anna.
* @date 2024/4/2 0:00
*/
public static <T> T get(Supplier<T> supplier){
return supplier.get();
}
}
案例3
package demo2;
import java.util.function.Supplier;
/**
* 使用Supplier<T>定义一个泛型方法,获取数组中的最大值
*
* @author Anna.
* @date 2024/4/1 23:57
*/
public class SupplierDemo2 {
public static void main(String[] args) {
// 定义一个数组
int[] arr = {123, 12, 123, 233, 1231};
Integer max = get(() -> {
int rtn = 0;
for (int item : arr) {
if (item > rtn) {
rtn = item;
}
}
return rtn;
});
System.out.println("max = " + max);
// 抽取方法
max = get(() -> getInteger(arr));
System.out.println("max = " + max);
}
/**
* 定义一个泛型方法,等到一个同类型的返回数据
*
* @param supplier
* @return T
* @author Anna.
* @date 2024/4/2 0:00
*/
public static <T> T get(Supplier<T> supplier) {
return supplier.get();
}
/**
* 提取成一个方法
*
* @param arr
* @return java.lang.Integer
* @author Anna.
* @date 2024/4/2 9:22
*/
private static Integer getInteger(int[] arr) {
int rtn = 0;
if (arr != null && arr.length > 0) {
for (int item : arr) {
if (item > rtn) {
rtn = item;
}
}
}
return rtn;
}
}
Consumer接口
Interface Consumer:包含两个方法:
- void accept(T t):对给定的参数执行操作
- default Consumer andThen(Consumer<? super T> after):Consumer组合依次执行andThen操作,然后执行after操作。
Consumer接口也被称为消费型接口,它消费的数据类型由泛型指定,不返回任何结果。
案例1
package demo3;
import java.util.function.Consumer;
/**
* String[] strArray = {"张三,数学,30","李四,语文,40","王五,体育,100"};
* 字符串数组中包含多条信息,请按照格式,“姓名:XXX,科目:XXX,分数:XXX”的格式将信息打印出来
* 要求:
* 把打印姓名的动作作为第一个Consumer接口的Lambda示例
* 把打印科目的动作作为第二个Consumer接口的Lambda示例
* 把打印分数的动作作为第三个Consumer接口的Lambda示例
* 将三个Consumer接口按照顺序组合到一起使用
*
* @author Anna.
* @date 2024/4/3 12:08
*/
public class ConsumerDemo {
public static void main(String[] args) {
String[] strArray = {"张三,数学,30", "李四,语文,40", "王五,体育,100"};
print(strArray,
s -> System.out.print("姓名:" + s.split(",")[0] + ","), // 第一个Consumer接口的Lambda
s -> System.out.print("科目:" + s.split(",")[1] + ","), // 第二个Consumer接口的Lambda
s -> System.out.println("分数:" + s.split(",")[2])); // 第三个Consumer接口的Lambda
}
public static void print(String[] arr, Consumer<String> com1, Consumer<String> com2, Consumer<String> com3) {
for (String str : arr) {
// 链式调用 等价于
// com1.accept(str);
// com2.accept(str);
// com3.accept(str);
com1.andThen(com2).andThen(com3).accept(str);
}
}
}
案例2
package demo3;
import java.util.function.Consumer;
/**
* 定义一个Config对象,通过函数式接口,完成数据的初始化
*
* @author Anna.
* @date 2024/4/3 12:08
*/
public class ConsumerConfig {
private String ip;
private Integer port;
/**
* 初始化对象
*
* @param consumer
* @return void
* @author Anna.
* @date 2024/4/3 12:25
*/
public void init(Consumer<ConsumerConfig> consumer) {
consumer.accept(this);
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(Integer port) {
this.port = port;
}
@Override
public String toString() {
return "ConsumerConfig{" +
"ip='" + ip + '\'' +
", port=" + port +
'}';
}
public static void main(String[] args) {
ConsumerConfig config = new ConsumerConfig();
System.out.println("未初始化之前:" + config);
config.init((consumer) -> {
consumer.setIp("127.0.0.1");
consumer.setPort(8080);
});
System.out.println("初始化之后:" + config);
}
}
Predicate接口
Interface Predicate:常用的四个方法:
- boolean test(T t):对给定的参数进行判断(判断逻辑有Lambda表达式实现),返回一个布尔值
- default Predicate and(Predicate<? super T> other):返回一个逻辑的否定,对应逻辑非(!),源码如下
default Predicate<T> negate() {return (t) -> !test(t);}
- default Predicate negate():返回一个组合判断,对应短路与(&&),源码如下
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
- default Predicate or(Predicate<? super T> other):返回一个组合判断,对应逻辑或(||),源码如下
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
案例1
package demo4;
import java.util.function.Predicate;
/**
* 使用函数式接口Predicate,完成以下校验
* 1 判断一个字符串长度是否大于5
* 2 判断一个字符串长度小于5且已S开头
* 3 判断一个字符串小于等于5
*
* @author Anna.
* @date 2024/4/3 14:10
*/
public class PredicateDemo {
public static void main(String[] args) {
// 判断一个字符串长度是否大于5
test01("12321", s -> s != null && s.length() > 5);
// 判断一个字符串长度小于5且已S开头
test02(null, s -> s != null && s.length() > 5, s -> s.startsWith("S"));
// 判断一个字符串小于等于5
test03("S2321", s -> s != null && s.length() > 5);
}
public static void test01(String str, Predicate<String> predicate) {
System.out.println(predicate.test(str));
}
public static void test02(String str, Predicate<String> pdc1, Predicate<String> pdc2) {
System.out.println(pdc1.and(pdc2).test(str));
}
public static void test03(String str, Predicate<String> pdc1) {
System.out.println(pdc1.negate().test(str));
}
}
Function接口
Interface Function<T,R>:常用的两个方法:
- R apply(T t):将此函数应用于给定的参数
- default Function<T,V> andThen(Function<? super R,? extends V> after):返回一个组合函数,首先将该函数应用于输出,然后将after函数应用于结果。源码如下:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
- Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),并返回一个新的值。
案例1
package demo5;
import java.util.function.Function;
/**
* 使用Function函数式接口:
* 1 将一个字符串数字,转换成int类型,输出在控制台
* 2 将一个字符串数字,转换成int类型,然后使用第二个Lambda表达式完成,加20,最后输出在控制台
* @author Anna.
* @date 2024/4/3 14:59
*/
public class FunctionDemo {
public static void main(String[] args) {
// 将一个字符串数字,转换成int类型,输出在控制台
print("213", Integer::parseInt);
// 将一个字符串数字,转换成int类型,然后使用第二个Lambda表达式完成,加20,最后输出在控制台
printAdd("100",Integer::parseInt,s -> s + 20);
}
public static void print(String str, Function<String,Integer> func){
System.out.println(func.apply(str));
}
public static void printAdd(String str, Function<String,Integer> func1,Function<Integer,Integer> func2){
System.out.println(func1.andThen(func2).apply(str));
}
}