函数式接口 就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口(default 修饰),这又关联了 Java 8 的另一个新特性,允许在接口中定义默认方法(default 修饰)。
函数式接口是行为的抽象,是数据转换。其最直接表现是将函数(行为)作为数据传递进方式中。
函数式编程的一大好处,是可以用更精练的代码来表达常用数据处理。函数接口能够轻易地实现模板方法模式,只要将不确定的业务逻辑抽象成函数接口,然后传入不同的 Lambda 表达式即可。
JDK 8 新特性中的 Lambda 表达式是 Java 8 发布的最重要的特性,Lambda 表达式允许把函数作为一个方法的参数传递 (函数作为参数传递进方法中),但是只能用在重写函数式接口的抽象方法上。
与 Lambda 表达式功能一起发布的 函数式接口 是其重要组成部分。Lambda 表达式和方法引用就是针对 函数式接口的使用(即只能用在函数式接口上),因为 Lambda 表达式没有方法名,所以针对的就是接口中的抽象方法,则要求函数式接口中有且只有一个抽象方法,否则就不知道 Lambda 重写的是哪个抽象方法。
Lambda 表达式只能用在重写函数式接口的抽象方法上,因为函数式接口只有一个抽象方法,编译器会认为 Lambda 表达式写的方法就是接口的唯一的抽象方法。
JDK 8 以前的接口中的所有方法必须都是抽象的,不能有非抽象方法,其实现类必须实现其所有方法,而 JDK 在更新过程中可能对原有的接口进行扩展,若在原有接口中加入抽象方法,那实现了该接口的类要全部重写新加入的方法,为了解决更新接口同时又兼容以前版本的程序,就支持在接口中增加 default 方法,变相的让 Java 支持多继承,因为可以实现多个接口。
Java 中的 Lambda 表达式的功能更多应用在流数据处理中(负责迭代数据的集合)。
FunctionalInterface
JDK 8 新增了 @FunctionalInterface
注解,用于标识接口类型声明为函数式接口,从概念上讲,函数式接口只有一个抽象方法。可以通过 java.lang.reflect.Method#isDefault() 反射操作来判断是否为默认方法。
注意:函数式接口的实现可以使用 Lambda 表达式,方法引用或函数引用创建。
@FunctionalInterface 注解只能作用在接口类型上,不能在注解类型,枚举类型 或 类上使用且。作用的目标接口必须满足函数式接口的要求。
@FunctionalInterface 注解用于显式标注一个函数式接口,该注解主要用于编译级错误检查,使用了该注解后,如果自定义接口不满足函数式接口的要求,会报错。并不是所有函数式接口都必须使用该注解进行标识,编译器会把满足函数接口定义的任何接口视为函数接口,而不管接口声明中是否存在 @FunctionalInterface 注解。
4个核心函数式接口
JDK 8 在 java.util.function 包中定义了一些基础的函数式接口,其中有四个可认为是核心的函数式接口:
函数式接口 | 类型别名 | 描述 | 备注 |
---|---|---|---|
Function<T,R> |
函数型 | 数据转换器,接收一个 T 类型的对象, 返回一个 R 类型的对象,单参数单返回值的行为接口; 提供了 apply, compose, andThen, identity 方法 |
|
Supplier<T> |
供给型 | 数据提供器,可以提供 T 类型对象; 无参的构造器,提供了 get 方法 |
|
Consumer<T> |
消费型 | 数据消费器, 接收一个 T 类型的对象,无返回值; 通常用于设置 T 对象的值; 单参数无返回值的行为接口;提供了 accept, andThen 方法 |
|
Predicate<T> |
断定型 | 条件测试器,接收一个 T 类型的对象,返回布尔值; 通常用于传递条件函数; 单参数布尔值的条件性接口; 提供了 test (条件测试) , and-or- negate(与或非) 方法; 其中, compose, andThen, and, or, negate 用来组合函数接口 而得到更强大的函数接口。 |
其它的函数接口都是通过这四个扩展而来:
- 在参数个数上扩展: 比如接收双参数的,有 Bi 前缀, 比如
BiConsumer<T, U>
,BiFunction<T, U, R>
。 - 在类型上扩展: 比如接收原子类型参数的,有 [
Int|Double|Long|Function|Consumer|Supplier|Predicate
]。 - 特殊常用的变形: 比如 BinaryOperator , 是同类型的双参数
BiFunction<T, T, T>
,二元操作符 。UnaryOperator 是Function<T, T>
一元操作符。
函数式接口可以接收的值类型有:
- 类/对象的静态方法引用、实例方法引用。引用符号为双冒号
::
。 - 类的构造器引用,比如
Class::new
。 - Lambda 表达式。
Consumer接口
接口定义
对入参做一些操作,没有返回值(相当于内部消费掉传入的参数)。
在 Stream 里,主要用于内部 ForEach,内部迭代,对传入的参数做一系列的业务操作。
/**
* 表示接受单个输入参数但不返回结果的操作
* 与大多数其他函数接口不同,Consumer 需要通过额外的作用来操作
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* 单一抽象方法
* 对给定参数执行此操作。
*/
void accept(T t);
/**
* 用于复合操作的默认方法
* 返回一个组合的{@code Consumer},按顺序执行此操作,然后执行{@code after}操作。
* 如果执行任一操作时引发异常,则将异常中继到组合操作的调用方。
* 如果执行此操作引发异常,则不会执行{@code after}操作。
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
使用示例
void accept(T t):
Lambda 表达式或方法引用相当于重写内部 accept(T t)
方法。
示例一:consumerTest1
@Test public void consumerTest1() { List<String> list = Arrays.asList("Tom", "Kitty", "Rose", "Alex", "Andy"); // 匿名内部类(重写内部 accept 方法, 默认调用单一的抽象方法) list.forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }); System.out.println("----------------------"); // Lambda 表达式(重写内部 accept 方法, 默认调用单一的抽象方法) list.forEach(s -> System.out.println(s)); System.out.println("----------------------"); // 方法引用(重写内部 accept 方法, 默认调用单一的抽象方法) list.forEach(System.out::println); }
结果输出:
Tom Kitty Rose Alex Andy ---------------------- Tom Kitty Rose Alex Andy ---------------------- Tom Kitty Rose Alex Andy
示例二:consumerTest2
/** * 1.消费型 Consumer<T> void accept(T t) */ @Test public void consumerTest2() { // 匿名方法 Consumer<String> c1 = new Consumer<String>() { @Override public void accept(String str) { System.out.println(str); } }; // Lambda 表达式 Consumer<String> c2 = str -> System.out.println(str); // 方法引用 Consumer<String> c3 = System.out::println; c3.accept("Hello World"); }
输出结果:
Hello World 1 Hello World 2 Hello World 3
示例三:consumerTest3
public void happy(double money, Consumer<Double> con) { consumer.accept(money); } @Test public void consumerTest3() { // Lambda this.happy(200D, s -> System.out.println(s)); // 方法引用 this.happy(200D, System.out::println); // 匿名方法 this.happy(200D, new Consumer<Double>() { @Override public void accept(Double money) { System.out.println(money); } }); }
输出结果:
200.0 200.0 200.0
andThen(Consumer<? super T> after)
示例一:consumerAndThen1
@Test public void consumerAndThen1() { String[] strArr = {"Tom:boy", "Kitty:girl", "Rose:girl"}; // 标准Lambda可以使用 lambda表达式替换 printInfo(strArr, (message) -> { System.out.print("Name:" + message.split(":")[0] + ""); }, (message) -> { System.out.println("Gender:" + message.split(":")[1] + ""); }); // Lambda 表达式 printInfo(strArr, (message) -> System.out.print("Name:" + message.split(":")[0] + ""), (message) -> System.out.println("Gender:" + message.split(":")[1] + "")); } // 传入多个 Consumer,指定执行顺序 public void printInfo(String[] strArr, Consumer<String> con1, Consumer<String> con2) { for (int i = 0; i < strArr.length; i++) { con1.andThen(con2).accept(strArr[i]); } }
输出结果:
Name:TomGender:boy Name:KittyGender:girl Name:RoseGender:girl Name:TomGender:boy Name:KittyGender:girl Name:RoseGender:girl
示例二:consumerAndThen2
@Test public void consumerAndThen2() { List<Integer> list = Arrays.asList(1, 3, 2, 6, 9, 5); Consumer<Integer> consumer = x -> System.out.print(x); // 组合 Consumer Consumer<Integer> consumerAndThen = consumer.andThen(i -> { System.out.println(", print:" + i); }); list.forEach(x -> consumerAndThen.accept(x)); System.out.println("----------------------"); list.forEach(consumerAndThen::accept); System.out.println("----------------------"); // 函数调用,调用默认的抽象方法accept list.forEach(consumerAndThen); System.out.println("----------------------"); for (Integer integer : list) { consumerAndThen.accept(integer); } }
输出结果:
1, print:1 3, print:3 2, print:2 6, print:6 9, print:9 5, print:5 ---------------------- 1, print:1 3, print:3 2, print:2 6, print:6 9, print:9 5, print:5 ---------------------- 1, print:1 3, print:3 2, print:2 6, print:6 9, print:9 5, print:5 ---------------------- 1, print:1 3, print:3 2, print:2 6, print:6 9, print:9 5, print:5
Supplier接口
Consumer 函数式接口相当于消费者,Supplier 接口就相当于生产者了,只有一个 get()
抽象方法返回结果。
接口定义
/**
* 表示结果的提供者
* 不要求每次调用 supplier 时都返回一个新的或不同的结果
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* 获取结果
*/
T get();
}
使用示例
T get()
public class SupplierTest {
@Test
public void supplierTest1() {
Supplier<String> supplier = String::new;
String string = supplier.get();
}
@Test
public void supplierTest2() {
Supplier<User> supplier = User::new;
User user = supplier.get();
}
@Test
public void supplierTest3() {
Supplier<String> supplier = () -> "Hello World";
System.out.println(supplier.get());
}
public List<Integer> getNumList(int num, Supplier<Integer> supplier) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = supplier.get();
list.add(n);
}
return list;
}
@Test
public void supplierTest4() {
List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
for (Integer num : numList) {
System.out.println(num);
}
}
}
Function接口
Function 接收参数,然后运算转换为结果返回。
接口定义
/**
* 表示接受一个参数并生成结果的函数
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* 将此函数应用于给定参数
*/
R apply(T t);
/**
* 返回一个组合函数, 先做入参 before 函数的apply操作, 然后做当前接口的apply操作
* 如果任何一个函数的求值引发异常, 它将被中继到组合函数的调用者
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 返回一个组合函数, 先做本接口数的apply操作, 然后做入参 after 函数的apply操作
* 如果任何一个函数的求值引发异常, 它将被中继到组合函数的调用者。
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* 静态方法,返回一个始终返回其输入参数的函数
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
使用示例
R apply(T t)
示例一
@Test public void functionTest1() { // 字符串 Function<Integer, Integer> fun1 = x -> x * 2; System.out.println(fun1.apply(4));// 8 Function<Integer, String> fun2 = x -> x * 2 + "aa"; System.out.println(fun2.apply(4));//8dd // 字符串对象 Function<String, String> strFun3 = (str) -> new String(str); System.out.println(strFun3.apply("bb"));//bb Function<String, String> strFun4 = String::new; System.out.println(strFun4.apply("cc"));//cc // 对象 Function<String, User> objFun5 = (str) -> new User(str); System.out.println(objFun5.apply("dd").getName());//dd Function<String, User> objFun6 = User::new; System.out.println(objFun6.apply("ee").getName());//ee } @Test public void functionTest2() { // 1.函数型 Function<T,R> R apply(T t) Function<String, Integer> function2 = s -> s.length(); Function<String, Integer> function1 = String::length; System.out.println(function1.apply("hello world")); System.out.println(function2.apply("hello world")); //结果:11 } // Function<T, R> 函数型接口 // 需求:用于处理字符串 public String strHandler(String str, Function<String, String> fun) { return fun.apply(str); } @Test public void functionTest3() { String newStr2 = strHandler("\t\t\t Hello World", (str) -> str.trim()); String newStr1 = strHandler("\t\t\t Hello World", String::trim); System.out.println(newStr1); System.out.println(newStr2); String subStr = strHandler("\t\t\t Hello World", (str) -> str.trim().substring(0, 4)); System.out.println(subStr); //Hello World //Hello World //Hell }
示例二
public class FunctionTest1 { public static void main(String[] args) { FunctionTest fun = new FunctionTest(); // 传递行为,而不是传递值 System.out.println(fun.comput(1, result -> 2 * num)); System.out.println(fun.comput(2, result -> 5 + num)); System.out.println(fun.comput(3, Integer::intValue)); System.out.println(fun.convert(4, result -> num + "helloworld")); } public int comput(int num, Function<Integer, Integer> function) { //apply ,传递的是行为 int result = function.apply(num); return result; } public String convert(int num, Function<Integer, String> function) { return function.apply(num); } // 对于之前只传递值的写法,几种行为就要定义几种写法。 现在可以使用上面的方式去 传递行为 public int method1(int num) { return num + 1; } public int method2(int num) { return num * 5; } public int method3(int num) { return num * num; } }
Predicate接口
接口定义
/**
* 一个参数的布尔值函数。
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* 根据给定参数计算布尔表达式
*/
boolean test(T t);
/**
* 返回一个组合的布尔表达式, 以表示 this 布尔表达式 与 other 布尔表达式的短路逻辑与(&&)
* 如果 this 为 false, 则不会评估
*/
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);
}
/**
* 返回一个组合的布尔表达式, 以表示 this 布尔表达式 与 other 布尔表达式的短路逻辑或(||)
* 如果 this 为 true, 则不会评估
*/
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) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
使用示例
boolean test(T t)
public class PredicatePreTest {
@Test
public void test1() {
//断定型 Predicate<T> boolean test(T t)
Predicate<Integer> predicate = num -> num >= 100;
System.out.println(predicate.test(99));
//false
}
@Test
public void test2() {
List<String> list = Arrays.asList("Tom", "Kitty", "Rose", "Andy");
List<String> strList = filterStr(list, (s) -> s.length() > 3);
for (String str : strList) {
System.out.println(str);
//Kitty
//Rose
//Andy
}
}
//需求:将满足条件的字符串,放入集合中去
public List<String> filterStr(List<String> list, Predicate<String> pre) {
List<String> strList = new ArrayList<>();
for (String str : list) {
if (pre.test(str)) {
strList.add(str);
}
}
return strList;
}
}
相关参考
注意:本文归作者所有,未经作者允许,不得转载