1. 定义
Java中有且仅有一个抽象方法的接口称为函数式接口,它主要应用于函数式编程的场景中。Java中函数式编程具体的体现就是Lambda表达式的使用,所以函数式接口可以适用于Lambda使用的接口。
- 只有确保接口中有且仅有一个抽象方法,接口才能被称为函数式接口
- 当然接口中可以有除抽象方法外其他的方法,如默认方法、静态方法和私有方法等
- 通常为保证函数式接口的正确性,通常用
@FunctionalInterface
注解来检测所编写的接口是否符合函数式接口的要求:
- 符合,编译成功
- 不符合,则编译失败,说明此时接口中没有抽象方法或者抽象方法的个数多于1
函数式接口格式:
修饰符 interface 接口名{
public abstract 返回值类型 方法名(参数列表){};
}
如一个简单的函数式接口可定义为:
@FunctionalInterface
public interface FuncInterface {
public abstract void method();
}
2. 使用
函数式接口一般作为方法的参数和返回值类型。例如在之前的 浅析Java和Python中Lambda表达式 中其实已经使用过函数式编程,在文中的例子中函数式接口作为方法的参数使用。下面我们再单独回顾下它作为方法参数的使用:函数式接口本质上仍然是接口,因此函数式接口的使用有三种方式:
- 定义接口的实现类并重写接口中的抽象方法,通过新建实现类的对象使用
public class FuncInterfaceImpl implements FuncInterface{
@Override
public void method() {
System.out.println("Functional interface...");
}
}
- 不显式的创建接口的实现类,而是通过匿名内部类的方式直接新建对象使用
- 通过函数式接口的的独有方法-Lambda表达式使用
public class FuncMain {
public static void main(String[] args) {
// 调用接口实现类的重写方法
show(new FuncInterfaceImpl());
// 使用匿名内部类重写方法
show(new FuncInterface() {
@Override
public void method() {
System.out.println("Functional interface...");
}
});
// 使用Lambda表达式重写方法
show(() -> {System.out.println("Functional interface...");});
show(() -> System.out.println("Functional interface..."));
}
public static void show(FuncInterface fi){
fi.method();
}
}
下面看一个使用函数式接口作为返回值类型的例子。ArrayList中的sort()
方法需要接收一个比较器,即Comparator,那么我们就将创建的Comparator作为返回值传入sort()
,然后再对ArrayList中的元素进行排序。
import java.util.ArrayList;
import java.util.Comparator;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Forlogen");
list.add("ada");
list.add("kobe");
System.out.println(list.toString());
list.sort(getComparator());
System.out.println(list.toString());
}
public static Comparator<String> getComparator(){
return (o1, o2) -> o2.length() - o1.length();
}
}
3. 特点
Lambda表达式除了可以简化代码的书写外,它还有一个重要的特点:延迟执行。怎么理解延迟执行呢?假设现在有一个日志输出的demo,只有日志等级等于1时才输出传入的日志信息,如果不使用Lambda表达式,代码编写如下:
public class LoggerDemo {
public static void main(String[] args) {
String m1 = "hello";
String m2 = " world";
String m3 = " ...";
show(1, m1 + m2 + m3);
}
public static void show(int level, String message){
if (level == 1){
System.out.println(message);
}
}
}
这样做有个问题:不管最后控制台是否会输出传入的日志信息,字符串的拼接工作都会执行,这样就造成了性能的浪费。而如果使用Lambda表达式作为参数传递,它仅仅是把参数传递到方法中,只有满足条件时才调用接口中的方法,然后进行字符串的拼接。如果条件不满足,接口中的方法就不会执行,那么自然不会进行字符串拼接,也就不会存在性能的浪费发生。
@FunctionalInterface
public interface MessageBuilder {
public abstract String building();
}
public class LambdaLoggerDemo {
public static void main(String[] args) {
String m1 = "hello";
String m2 = " world";
String m3 = " ...";
showLog(1, () ->{return m1 + m2 + m3;});
// showLog(2, () ->{return m1 + m2 + m3;});
}
public static void showLog(int level, MessageBuilder mb){
if (level == 1){
System.out.println(mb.building());
}
}
}
4. Java中的函数式接口
除了可以根据规则自定义和使用函数式接口外,Java的java.util.function包中提供了大量已编写好的函数式接口供用户使用。
4.1 Supplier<T>
@FunctionalInterface
public interface Supplier<T> {
T get();
}
java.util.function.Supplier<T> 接口仅含有一个无参的方法T get()
,它用来获取一个泛型参数指定类型的对象数据。因此,Supplier接口又被称为生产者接口,通过指定接口中泛型的具体类型,接口就会生产对应类型的数据。
import java.util.function.Supplier;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
String s = getString(()-> "Forlogen");
System.out.println(s); // Forlogen
}
public static String getString(Supplier<String> sup){
return sup.get();
}
}
如上所示,Java程序使用Supplier接口获取一个String类型的数据。因为我们可以在Lambda表达式中编写自己的逻辑,只要最后是获取某一具体类型的数据,中间过程可以做任何的操作。例如,使用Supplier接口实现获取数组中最大值的操作:
import java.util.function.Supplier;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
int[] array = {1, 3, 10, 5, 8};
// 自动拆箱
int max = getMax(() -> {
int maxN = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > maxN) {
maxN = array[i];
}
}
return maxN;
});
System.out.println(max); // 10
}
public static Integer getMax(Supplier<Integer> sup){
return sup.get();
}
}
4.2 Consumer<T>
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
java.util.function.Consumer<T>接口用于消费一个由泛型指定的具体类型数据,接口中的抽象方法为void accept(T t)
,用来消费一个指定类型的数据。虽然接口用来消费某一类型的数据,但具体怎么消费或者说如何使用数据的逻辑由用户自定义。例如,我们可以使用Consumer接口消费一个字符串,消费过程是将字符串中的字母全部转为大写形式。
import java.util.function.Consumer;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
Consume("Forlogen", (name) -> System.out.println(name.toUpperCase())); //FORLOGEN
}
public static void Consume(String s, Consumer<String> c){
c.accept(s);
}
}
如果我们的消费过程涉及到多步操作呢?即如果需要对每次消费的结果再次进行消费呢?按照一般的写法,我们要定义多个Consumer接口,然后分别调用accept()
进行消费。除此之外,Consumer接口中提供了一个默认方法addThen()
用于将多个接口组合在一起,然后再对数据进行消费。以String类型的数据为例,方法的使用格式为:
String str = " ...";
Consumer<String> c1, Consumer<String> c2, COnsumer<String> c3; // 可以是多个
c1.addThen(c2).andThen(c3).accept(s)
- 理论上可以使用
addThen()
组合无限多个Consumer接口使用,只需要不断地·andThen()
即可,最后使用accept()
消费数据- 使用中,谁写在前边谁就先消费,后面的接口消费前一个接口的结果
例如我们实现简单的将字符串先转为大写,然后再转为小写,就可以使用addThen()
组合两个Consumer接口消费两次。
import java.util.function.Consumer
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
ConsumeAddThen("Forlogen", (name)-> System.out.println(name.toUpperCase()),
(name)->System.out.println(name.toLowerCase()) // FORLOGEN forlogen
);
}
public static void ConsumeAddThen(String s, Consumer<String> c1, Consumer<String> c2){
c1.andThen(c2).accept(s);
}
}
4.3 Predicate<T>
@FunctionalInterface
public interface Predicate<T> {
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);
}
}
java.util.function.Predicate<T>接口用于对某种数据类型的数据进行判断,接口中包含一个抽象方法:boolean test(T t)
用来对指定的数据类型进行判断:
- 如果符合条件,返回true
- 如果不符合条件,返回false
至于条件如何设置由用户自定义。例如,使用Predicate接口判断字符串的长度是否大于5:
import java.util.function.Predicate;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
boolean b = CheckString("Forlogen", (str) -> str.length() > 5); // true
System.out.println(b);
}
public static boolean CheckString(String s, Predicate<String> p){
return p.test(s);
}
}
类似于逻辑判断中的&&
、||
和 !
,Predicate接口中提供了默认方法and()
、or()
和negate()
来实现同样的逻辑。
import java.util.function.Predicate;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
boolean b1 = CheckStringAnd("Forlgoen", (str)-> str.startsWith("a"), (str)->str.length() > 5);
System.out.println(b1); // false
boolean b2 = CheckStringOr("Forlgoen", (str)-> str.startsWith("a"), (str)->str.length() > 5);
System.out.println(b2); // true
boolean b3= CheckStringNegate("Forlgoen", (str)-> str.startsWith("a"));
System.out.println(b3); // true
}
public static boolean CheckStringAnd(String s, Predicate<String> p1, Predicate<String> p2){
return p1.and(p2).test(s);
// 等价于 return p1.test(s) && p2.test(s);
}
public static boolean CheckStringOr(String s, Predicate<String> p1, Predicate<String> p2){
return p1.or(p2).test(s);
// 等价于 return p1.test(s) || p2.test(s)
}
public static boolean CheckStringNegate(String s, Predicate<String> p){
return p.negate().test(s);
// 等价于 return !p.test(s);
}
}
4.4 Function<T, R>
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
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));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
java.util.function.Function<T, R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。接口中的抽象方法R apply(T t)
用于根据类型T的参数获取类型R的结果,将进行数据类型的转换。如将String类型数据转换为Integer类型:
import java.util.function.Function;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
int x = convert((str)-> Integer.parseInt(str), "100");
System.out.println(x); // 100
}
public static Integer convert(Function<String, Integer> f, String s){
return f.apply(s);
}
}
Function接口中同样提供了addThen()
来执行多步操作。
import java.util.function.Function;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
String s1 = convertAndThen((str) -> Integer.parseInt(str) * 3, (num) -> num + " ", "1000");
System.out.println(s1); // 3000
}
public static String convertAndThen(Function<String, Integer> f1, Function<Integer, String> f2, String s){
return f1.andThen(f2).apply(s);
}
}
完整实验代码:
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class SomeFncInterfacesInJDK {
public static void main(String[] args) {
// Supplier接口的使用
String s = getString(()-> "Forlogen");
System.out.println(s); // Forlogen
// 使用Supplier实现获取数组的最大值
int[] array = {1, 3, 10, 5, 8};
/*
自动拆箱,等价于
Integer max = getMax();
System.out.println(max.intValue());
*/
int max = getMax(() -> {
int maxN = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > maxN) {
maxN = array[i];
}
}
return maxN;
});
System.out.println(max); // 10
// Consumer接口的使用
Consume("Forlogen", (name) -> System.out.println(name.toUpperCase())); //FORLOGEN
ConsumeAddThen("Forlogen", (name)-> System.out.println(name.toUpperCase()),
(name)->System.out.println(name.toLowerCase()) // FORLOGEN forlogen
);
// Predicate接口的使用
boolean b = CheckString("Forlogen", (str) -> str.length() > 5); // true
System.out.println(b);
boolean b1 = CheckStringAnd("Forlgoen", (str)-> str.startsWith("a"), (str)->str.length() > 5);
System.out.println(b1); // false
boolean b2 = CheckStringOr("Forlgoen", (str)-> str.startsWith("a"), (str)->str.length() > 5);
System.out.println(b2); // true
boolean b3= CheckStringNegate("Forlgoen", (str)-> str.startsWith("a"));
System.out.println(b3); // true
// Function接口的使用
int x = convert((str)-> Integer.parseInt(str), "100");
System.out.println(x); // 100
String s1 = convertAndThen((str) -> Integer.parseInt(str) * 3, (num) -> num + " ", "1000");
System.out.println(s1); // 3000
}
public static String getString(Supplier<String> sup){
return sup.get();
}
public static Integer getMax(Supplier<Integer> sup){
return sup.get();
}
public static void Consume(String s, Consumer<String> c){
c.accept(s);
}
public static void ConsumeAddThen(String s, Consumer<String> c1, Consumer<String> c2){
c1.andThen(c2).accept(s);
// c1.accept(s);
// c2.accept(s);
}
public static boolean CheckString(String s, Predicate<String> p){
return p.test(s);
}
public static boolean CheckStringAnd(String s, Predicate<String> p1, Predicate<String> p2){
return p1.and(p2).test(s);
// 等价于 return p1.test(s) && p2.test(s);
}
public static boolean CheckStringOr(String s, Predicate<String> p1, Predicate<String> p2){
return p1.or(p2).test(s);
// 等价于 return p1.test(s) || p2.test(s)
}
public static boolean CheckStringNegate(String s, Predicate<String> p){
return p.negate().test(s);
// 等价于 return !p.test(s);
}
public static Integer convert(Function<String, Integer> f, String s){
return f.apply(s);
}
public static String convertAndThen(Function<String, Integer> f1, Function<Integer, String> f2, String s){
return f1.andThen(f2).apply(s);
}
}