Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解放在接口上,表示此接口是一个函数式接口。并且提示编译器去检查接口是否仅包含一个抽象方法,即,是否符合函数式编程的定义。
技术的升级往往不是独立的,而是一次系统性的升级,小部分升级通常是改BUG,JDK8的升级意义非常重大,各个升级环环相扣!本篇介绍的函数式接口和上篇讲解的《Lambda表达式》紧密相关!本篇你只需要搞懂什么是函数式接口这个概念就行啦,代码写不写无所谓,非常简单!
掌握内容
- 函数式接口概念和意义
- 认识JDK内置函数式接口
- 函数式接口配合Lambda实现
- 自定义函数式接口
- @FunctionalInterface注解作用
函数式接口
函数式接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,是Lambda表达式的实现前提,可以使用@FunctionalInterface注解修饰
函数式接口意义
Java一直倡导面向对象,随着Python、Scala等语言的兴起和新技术的挑战,Java必须调整来支持更加广泛的技术要求,所以Java不单单OOP【面向对象编程】同样支持OOF【面向函数编程】。
以往需要通过匿名内部类实现,现在都可以通过Lambda实现,其实Lambda表达式可以看做是一个函数式接口的实例。
JDK内置函数式接口
注:不需要掌握,不需要掌握!我看网上很多资料都只写了四个内置接口,比较局限,这里对JDK内置接口做一个全面的说明,只需要知道有这么多内置接口,并不是只有四个就可以了。
JDK8新增函数式接口:
JDK8新推出的函数式接口在java.util.function包下
作用如下:
函数式接口 | 参数类型 | 返回类型 | 用途 |
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get() |
Function<T, R>函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t) |
Predicate断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法:boolean test(T t) |
BiFunction<T,U,R> | T, U | R | 对类型为T,U参数应用操作,返回R类型的结果。包含方法为:Rapply(T t,U u) |
UnaryOperator(Function子接口) | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为:Tapply(T t); |
BinaryOperator(BiFunction子接口) | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为:Tapply(T t1,T t2); |
BiConsumer<T,U> | T,U | void | 对类型为T,U参数应用操作。包含方法为:voidaccept(Tt,Uu) |
BiPredicate<T,U> | T,U | boolean | 包含方法为:booleantest(Tt,Uu) |
ToIntFunction | T | int | 计算int值的函数 |
ToLongFunction | T | long | 计算long值的函数 |
ToDoubleFunction | T | double | 计算double值的函数 |
IntFunction | int | R | 参数为int类型的函数 |
LongFunction | long | R | 参数为long类型的函数 |
DoubleFunction | double | R | 参数为double类型的函数 |
JDK8之前的函数式接口:
JDK8之前也存在函数式接口,在JDK8升级之后这些接口头部都加上了@FunctionalInterface修饰,如下:
- java.lang.Runnable【熟悉吧,创建线程】
- java.util.concurrent.Callable【创建线程】
- java.security.PrivilegedAction【执行计算】
- java.util.Comparator【Lambda一篇说过的 比较器】
- java.io.FileFilter【文件过滤器】
- java.nio.file.PathMatcher【路径匹配】
- java.lang.reflect.InvocationHandler【动态代理】
- java.beans.PropertyChangeListener【属性变化监听器】
- java.awt.event.ActionListener【事件监听器】
- javax.swing.event.ChangeListener【change事件监听】
函数式接口使用
在上篇中我们已经使用过Runnable、Consumer、Supplier、Comparator等接口,这里我们再使用Function和Predicate接口,其他接口如果用到了可以照葫芦画瓢即可!
Function接口
接口定义如下:
小贴士:接口中有且仅有一个抽象方法的接口就是一个函数式接口,和默认实现以及静态方法无关。
package java.util.function;
import java.util.Objects;
/**
*
* @param <T> 输入参数类型
* @param
@FunctionalInterface
public interface Function<T, R> {
/**
抽象方法:输入T类型参数,返回R类型的值
T和R是泛型哦,小伙伴不要搞混
*/
R apply(T t);
/**
JDK8新特性,接口中可以存在默认实现
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before){
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
默认实现
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after){
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
JDK8接口新特性:可以有静态方法
*/
static <T> Function<T, T> identity(){
return t -> t;
}
}
接口特点:有一个输入参数和一个输出参数,也就是一进一出,如果你有需求是传入一个参数并返回一个参数的需求可以使用该接口实现。
需求:
实现一个字符串转换功能,将输入的英文字符都转换为大写返回。
分析:
输入和输出数据都是字符串所有泛型类型均为String。
调用 apply方法进行计算之后接收返回值。
代码实现:
public class FunctionMain {
public static void main(String[] args) {
// 1、原始匿名内部类写法
Function<String, String> function1 = new Function<String, String>() {
@Override
public String apply(String inputStr) {
// 转换为大写
return inputStr.toUpperCase();
}
};
String result = function1.apply("Just give me a chance to start!");
System.out.println(result);
// 2、Lambda表达式写法
Function<String,String> function2 = inputStr -> inputStr.toUpperCase();
String lambdaResult = function2.apply("Lambda really smells good!");
System.out.println(lambdaResult);
}
}
Predicate接口
接口定义:
该接口也存在默认实现和静态方法,但是只有一个抽象方法,所以也是一个函数式接口。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
/**
根据参数输入判断是否正确,返回true或者false
*/
boolean test(T t);
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);
}
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);
}
}
接口特点:该接口根据传入数据通过计算之后返回true或者false,如果你想要做单个参数的判断可以使用该接口。
小贴士:Java中有两个Predicate类,不要导错包,认准java.util.function包,当然自定义的类也不要起这个名字,【有许多初学者喜欢起同名的类】。
需求:判断输入的数据是否大于0。
分析:
- 泛型定义为Integer类型。
- 通过判断返回结果即可。
代码实现:
public class PredicateMain {
public static void main(String[] args){
// 1、原始实现方式
Predicate predicate1 = new Predicate<Integer>() {
@Override
public boolean test(Integer num){
return num > 0;
}
};
// 调用test方法
boolean result = predicate1.test(1024);
System.out.println(result);
// 2、Lambda表达式实现
Predicate<Integer> predicate2 = num -> num > 0;
// 调用test方法
boolean lambdaResult = predicate2.test(-1024);
System.out.println(lambdaResult);
}
}
小贴士:这些默认方法的接口,使用时不要调用错方法就行!
自定义函数式接口
分析:
函数式接口就是有且仅有一个抽象方法,默认实现和静态方法不影响它是一个函数式接口【JDK8支持接口有默认方法和静态方法】。
接口,定义抽象即可,所以我这里都使用泛型,可以根据自己的需求定义,如果需求要限制类型也可以直接定义成具体的类型。
接口定义:
package com.stt.function.myfunction;
/**
* 自定义函数式接口:
* 定义:
* 1、接口中只有一个抽象方法
* 2、可以使用@FunctionInterface注释修饰,也可以不使用
* 如果使用该注解报错,说明该接口不是一个函数式接口
*/
@FunctionalInterface
public interface SttFunction<T,R,V> {
/**
* 接收两个参数,并返回一个参数
* 注意:接口嘛,定义个大概就行了,具体什么参数,怎么返回就不需要说明了,具体实现的时候再说呗
*/
V calc(T t,R r);
}
接口使用:
package com.stt.function.myfunction;
public class SttFunctionMain {
public static void main(String[] args) {
// 1、原始方式,匿名内部类实现
SttFunction<Integer, Integer, Integer> sttFunction1 = new SttFunction<Integer, Integer, Integer>() {
@Override
public Integer calc(Integer num1, Integer num2) {
return num1 * num2;
}
};
Integer result = sttFunction1.calc(2, 2);
System.out.println(result);
// 2、Lambda表达式调用
SttFunction<Integer,Integer,Integer> sttFunction = (num1,num2) -> num1 + num2;;
Integer lambdaResult = sttFunction.calc(1023, 1);
System.out.println(lambdaResult);
}
}
Lambda表达式就是香。
包含默认实现的函数式接口:
包含默认方法和静态方法并不影响它是一个函数式接口。
package com.stt.function.myfunction;
/**
* 自定义函数式接口:
* 定义:
* 1、接口中只有一个抽象方法
* 2、可以使用@FunctionInterface注释修饰,也可以不使用
* 如果使用该注解报错,说明该接口不是一个函数式接口
*/
@FunctionalInterface
public interface SttFunction<T,R,V> {
/**
* 接收两个参数,并返回一个参数
* 注意:接口嘛,定义个大概就行了,具体什么参数,怎么返回就不需要说明了,具体实现的时候再说呗
*/
V calc(T t,R r);
default void defaultMethod(){
System.out.println("也不知道实现点什么,反正JDK8之后可以有默认实现!");
}
static void staticMethod(){
System.out.println("同样不知道写点什么,反正JDK8之后可以有静态方法!");
}
}
有多个抽象方法:
有两个以上抽象方法就不再是一个函数式接口,所以@FunctionalInterface注解报错,该注解可以用来检验接口是否为一个函数式接口。
@FunctionalInterface注解
Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解放在接口上,表示此接口是一个函数式接口。并且提示编译器去检查接口是否仅包含一个抽象方法,即,是否符合函数式编程的定义。
小贴士:如果自定义一个符合规范的函数式接口,也可以不加@FunctionalInterface注解,此注解只是起到一个提示编译器进行规范检查的作用
总结
- 技术升级都是系统性的,仅升级修改某一部分通常是修复BUG。
- 函数式接口是Lambda的前提,JDK8之前通过匿名内部类实现,Lambda让编码变的简洁。
- 函数式接口中有且仅有一个抽象方法。
- 函数式接口可以使用@FunctionalInterface检验,也可以不使用该注解。
- JDK内置了许多函数式接口,可以按需使用,我们也可以自定义函数式接口。
- 在阅读部分框架源码时一定要认识Lambda表达式和函数式接口哦。
文章出自:添甄,如有转载本文请联系【添甄】今日头条号。