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包中提供了大量已编写好的函数式接口供用户使用。



dao层的接口实现函数 java java函数接口应用场景_函数式接口


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);
    }
}