Java函数式接口

Java函数式接口

  • 一、函数式接口概念
    • @FunctionalInterface注解
    • 函数式接口的实现
      • 直接实现
      • 匿名内部类
      • Lambda表达式
  • 二、常见的函数式接口
    • 2.1 Supplier
    • 2.2 Consumer接口
    • 2.3 Predicate接口
    • 2.4 Function接口
  • 三、总结

一、函数式接口概念

首先什么是函数式接口?

定义:函数式接口就是一个有且仅有一个抽象方法,但可以有多个非抽象方法的接口

函数式接口是适用于函数式编程的接口,在Java中最直接的体现就是可以使用Lambda表达式

@FunctionalInterface注解

在Java8中引入了@FunctionalInterface注解来标识函数式接口,而且该注解还可以进行编译错误检查。

加上@FunctionalInterface注解后,当接口不符合函数式接口定义时,编译器会报错。

几个函数式接口实例:

  • 只有一个抽象方法的函数式接口
/**
 * 这就是个函数式接口,接口只有一个抽象方法
 */
@FunctionalInterface
public interface Show {
    void show();
}
  • 有一个抽象方法和多个非抽象方法的函数式接口
/**
 * 这是个函数式接口,接口有一个抽象方法和一个非抽象方法
 */
@FunctionalInterface
public interface Show {
    void show();

    default void printTest() {
        System.out.println("Hello CSDN");
    }
}
  • 有多个抽象方法时不再是函数式接口,编译器报错
/**
 * 有两个抽象方法时,不再是函数式接口
 */
@FunctionalInterface
public interface Show {
    void show();

    void read();

    default void printTest() {
        System.out.println("Hello CSDN");
    }
    /** 
     * 编译器报错
	 * java: 意外的 @FunctionalInterface 注释
	 *   test.Show 不是函数接口
	 *     在 接口 test.Show 中找到多个非覆盖抽象方法
	 */
}

函数式接口的实现

我们已经知道函数式接口会包含一个抽象方法,当使用接口时要进行实现,实现函数式接口有三种方案:直接实现、匿名内部类、Lambda表达式

  • 直接实现

public class ShowImpl implements Show{
    @Override
    public void show() {
        System.out.println("Hello 直接继承接口实现");
    }
}
  • 匿名内部类

public static void main(String[] args) {
    Show test01 = new Show() {
        @Override
        public void show() {
            System.out.println("Hello 匿名内部类");
        }
    };
    test01.show();
}
  • Lambda表达式

public static void main(String[] args) {
     Show test01 = () -> System.out.println("Hello Lambda表达式");
     test01.show();
 }

对比以上三种实现方法,很明显Lambda表达式是最简便的,Lambda表达式是Java8引入的特性,具有易读、高效、延迟实现的优点,推荐大家使用

二、常见的函数式接口

Java8在java.util.function包下定义了非常多函数式接口

Java函数式接口_第1张图片
这里介绍几个常用的

  • Supplier接口
  • Consumer接口
  • Predicate接口
  • Function接口

2.1 Supplier

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
  • T get():抽象函数没有参数,可以获取结构,需要自己实现内部逻辑
  • Supplier接口也被称为生产型接口,通过指定泛型的类型,可以生产对应类型的数据供我们使用

案例:

public class Demo {
    public static String getString(Supplier<String> supplier) {
        return supplier.get();
    }

    public static void main(String[] args) {
        Supplier supplier = () -> "hello world";
        String res = getString(supplier);
        System.out.println(res);
    }
}

2.2 Consumer接口

@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); };
    }
}
  • void accept(T t):Consumer的抽象函数有参数无返回值
  • Consumer andThen(Consumer after):包含一个非抽象函数,其参数和返回值都是Consumer
  • Consumer接口和Supplier接口是相反的,Consumer接口被称为消费型接口,通过泛型来指定要传入参数的类型,然后进行处理消费,没有任何返回值

案例一:只有accept(T t)函数

public static void main(String[] args) {
    List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
    
    // 打印最大值
    Consumer<List<Integer>> printMax = (numList) -> {
        int maxNum = Integer.MIN_VALUE;
        for (Integer num : numList) {
            maxNum = Integer.max(num, maxNum);
        }
        System.out.println(maxNum);
    };
    printMax.accept(nums);
}

案例二:包含andThen

public static void main(String[] args) {
    List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);

    // 打印最大值
    Consumer<List<Integer>> printMax = (numList) -> {
        int maxNum = Integer.MIN_VALUE;
        for (Integer num : numList) {
            maxNum = Integer.max(num, maxNum);
        }
        System.out.println(maxNum);
    };

    // 打印最小值
    Consumer<List<Integer>> printMin = (numList) -> {
        int minNum = Integer.MAX_VALUE;
        for (Integer num : numList) {
            minNum = Integer.min(num, minNum);
        }
        System.out.println(minNum);
    };

    // 先printMax进行消费,后printMin进行消费
    printMax.andThen(printMin).accept(nums);
}

2.3 Predicate接口

@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);
    }
}
  • boolean test(T t):通过泛型指定传入参数类型,返回true/false,Predicate是测试接口
  • Predicate的默认方法实现了组合的复杂逻辑(与、或、非)

案例:

public static void main(String[] args) {
    String testString = "hello world";
    // 判断s长度是否大于5
    Predicate<String> lenPredicate = (s) -> s.length() > 5;
    System.out.println(lenPredicate.test(testString));
    // 并且判断是否包含字符'a'
    Predicate<String> hPredicate = (s) -> s.contains("a");
    // and方法,长度既大于5又包含a
    System.out.println(lenPredicate.and(hPredicate).test(testString));
    // or方法,长度大于5或者包含a
    System.out.println(lenPredicate.or(hPredicate).test(testString));
    // negate, 长度不大于5
    System.out.println(lenPredicate.negate().test(testString));
}

2.4 Function接口

@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;
    }
}
  • R apply(T t):可以通过泛型来指定参数和返回值的类型
  • Function接口通常用于对参数进行处理、转换,然后返回一个新的值
public static void main(String[] args) {
    Function<String, Integer> stringIntegerFunction = (str) -> Integer.valueOf(str);
    Function<Integer, String> integerStringFunction = (i) -> i.toString();
    Integer num = 46;
    // 测试apply方法
    String t1 = integerStringFunction.apply(num);
    // 测试compose,先运行integerStringFunction,再运行stringIntegerFunction
    Integer t2 = stringIntegerFunction.compose(integerStringFunction).apply(num);
    // 测试andThen,先运行integerStringFunction,再运行stringIntegerFunction
    Integer t3 = integerStringFunction.andThen(stringIntegerFunction).apply(num);
}

以上介绍的几种都是jdk8的函数式接口,实际上jdk8之前也有许多函数式接口,如java.lang.Runnable、java.util.Comparator等,这些函数式的接口用法都是相同的,不再赘述了。

三、总结

函数式接口是一种功能性接口,将函数式接口和Lambda表达式结合使用可以让代码更简洁易读,非常方便开发和维护,希望大家今后可以多多实践。

你可能感兴趣的:(Java基础,java)