Java 8 函数式接口

概述

Java 8 的函数式编程是在吸收了其他语言的精髓后得到的产物,在原有的基础上新增了更多抽象的函数式接口,便于开发者实现高效优雅的编码。

此前常见的函数式接口包括 RunnableCallableComparator 等,新增的函数式接口定义在 java.util.function 包中,常见的基本接口主要包括 predicateConsumerSupplierFunction,他们的含义以及使用特征大致概括如下表:

接口名含义入参出参形式
Predicate谓词型,又称断言型接收一个入参,返回一个布尔值
Consumer消费型接收一个入参,无出参
Supplier供给型无入参,返回一个出参
Function<T, R>功能型接收一个入参,返回一个出参

在 JDK8 之前,实现函数式接口的方式主要是通过重新匿名内部类,最经典的例子就是 Comparator 接口中 compare方法的重写。自 JDK8 开始,我们就可以通过 lambda 表达式更加简洁地实现函数式接口,优化了原来的匿名内部类的形式。

通过查看源码发现,在函数式接口下共有三种类型的方法:

  • 唯一的抽象方法。接口内如果存在其他方法,都是基于这个抽象方法进行拓展的。
  • 使用 default 定义的普通方法(默认方法)。如果接口中的默认方法不能满足某个实现类需要,那么实现类可以覆盖默认方法。签名跟接口 default 方法一致,但是不能再使用 default 修饰符。
  • 使用 static 修饰的静态方法,可直接调用。

如果想要定义一个函数式接口,可以使用注解 @FunctionInterface,只定义一个抽象方法即可。

应用实例

函数式接口在实际应用中,其本质就是将一个函数的表达当作一个参数进行传递和处理,也就是对方法进行引用,因此使用 lambda 表达进行实例化能更加高效地进行编码。

Predicate

谓词型(断言型)接口用来判断入参的泛型 T 对象是否符合特定条件,如果符合则返回 true,否则返回 false。该接口源码中定义了一个抽象方法 test:

boolean test(T t);

另外还提供了诸如与、或、非操作的方法,通过入参传入其他 predicate 方法,实现多个条件相结合:

default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
}
default Predicate<T> negate() { return (t) -> !test(t); }
static <T> Predicate<T> isEqual(Object targetRef) {
    return (null == targetRef)
        ? Objects::isNull 
        : object -> targetRef.equals(object);
}
static <T> Predicate<T> not(Predicate<? super T> target) {
    Objects.requireNonNull(target);
    return (Predicate<T>)target.negate();
}

由于接口不能直接实例化,所以可以通过内部匿名类或者 lambda 表达式进行实例化。下面以 lambda 表达式为例:

// 满足给定条件的字符串将其打印出来,否则打印"nothing"
void demoPredicate() {
    String str = "demo string for predicate test";
    printResult(str, s -> length(s) > 20);  // 长度大于20才打印
}
<T> void printResult(String str, Predicate<T> predicate) {
    predicate.test(str) ? System.out.println(str) : System.out.println("nothing");
}

在 java.util.function 包中还定义了针对特定基本类型的断言型接口,其用法跟上述基本一致,因此不再作详细说明。

Consumer

消费型接口顾名思义,消耗接收的一个泛型 T 入参,执行操作后,不返回结果。其源码定义了一个抽象方法 accept 以及一个 andThen 方法:

void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t);  after.accept(t);};
}

我们把上面的例子做一点修改,将打印字符串这个操作作为一个入参:

void demoConsumer() {
    String str = "demo string for consumer test";
    consumeAction(str, s -> System.out.println(s));
}
<T> void consumeAction(String str, Consumer<T> consumer) {
    consumer.accrpt(str);
}

Supplier

供给型可以生成一个泛型 T 对象进行返回,无需接收入参。其源码定义了一个抽象方法 get:

T get();

以获取当前系统时间作为例子进行说明:

void demoSupplier() {
    Long time = supply(() -> System.currentTimeMillis());
}
<T> T supply(Supplier<T> supplier) {
    return supplier.get();
}

Function

功能型接口,该单词本身也是函数的意思,其接收一个泛型 T 对象,返回一个泛型 R 对象。其源码定义了一个基础的抽象方法 apply 以及其他延伸方法:

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

// 返回一个函数对象,其执行了apply()方法只会返回入参。
static <T> Function<T, T> identity() {
    return t -> t;
} 

以对字符串添加指定前缀后再获取长度作为例子进行说明:

void demoFunction() {
    String str = "demo string for function test";
    System.out.println(func(str, s -> "Content: " + s), String::length);
}
<T, R> R func(T t, Function<T, T> f1, Function<T, R> f2) {
    // 先执行f1(添加前缀),后执行f2(获取长度)
    return f1.andThen(f2).apply(t);
}

在 Function 接口之下,还有有一些常用的子接口,如 UnaryOperatorBinaryOperator,在一定程度上拓展了 Function 接口的功能。

UnaryOperator 是一元操作符的意思,该接口接收一个泛型 T 对象,返回相同类型的 T 对象。从源码中可以发现,UnaryOperator 继承了 Function 接口,定义了一个方法 identity:

static <T> UnaryOperator<T> identity() {
    return t -> t;
}

BinaryOperator 是二元操作符的意思,该接口接收两个相同的泛型 T 对象,返回一个泛型 R 对象。从源码中可以看出,其继承了 BiFunction 接口,并定义了 apply 方法:

R apply(T t, U u);

由于这两个接口继承了 Function 接口,因此同样可以使用 apply 方法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Java 8 函数式接口是一种拥有单个抽象方法的接口,它可以被隐式地转换为 lambda 表达式。您可以使用 @FunctionalInterface 注解声明一个接口为函数式接口。这样做可以帮助编译器检测到您是否正确地在接口中声明了单个抽象方法。 例如: ``` @FunctionalInterface public interface Converter<F, T> { T convert(F from); } ``` 这是一个函数式接口,因为它只有一个抽象方法 `convert()`。您可以使用 lambda 表达式来实现这个接口: ``` Converter<String, Integer> converter = (from) -> Integer.valueOf(from); Integer converted = converter.convert("123"); System.out.println(converted); // 123 ``` 函数式接口使用起来非常方便,因为它们可以被隐式地转换为 lambda 表达式。这使得您可以使用更简洁的代码来实现接口,而无需显式地创建一个类来实现该接口。 ### 回答2: Java 8引入了函数式接口,这是一种只有一个抽象方法的接口。函数式接口提供了一种简洁的方式来定义Lambda表达式,Lambda表达式可以作为函数的参数使用。 函数式接口可以通过使用@FunctionalInterface注解来明确标识。这个注解是可选的,但建议使用,因为它可以确保接口只有一个抽象方法,防止不必要的错误。 Java 8提供了一些内置的函数式接口,用于处理常见的函数式编程场景。其中一些常用的函数式接口包括: 1. Predicate(断言):代表一个谓词(布尔类型函数),接受一个参数,返回一个布尔值结果。 2. Consumer(消费者):代表一个消费者(接受一个参数并执行某些操作),接受一个参数,不返回任何结果。 3. Function(函数):代表一个函数(接受一个参数并返回一个结果),接受一个参数并返回一个指定类型的结果。 4. Supplier(供应者):代表一个供应者(不接受参数但返回一个结果),不接受任何参数,返回一个指定类型的结果。 这些函数式接口的引入使得Java 8具备了更强大的函数式编程能力,使代码更加简洁和易读。通过结合Lambda表达式和函数式接口,我们可以实现与函数式编程语言类似的编程风格,提高代码的可读性和可维护性。 ### 回答3: Java 8 引入了函数式接口的概念。函数式接口是一个只有一个抽象方法的接口。在 Java 8 之前,我们需要定义一个接口,并在其中添加一个抽象方法,才能够当作 Lambda 表达式的参数进行传递。而在 Java 8 中,我们只需使用 @FunctionalInterface 注解来标注一个接口,就可以将其定义为函数式接口了。这使得我们能够更加简洁地使用 Lambda 表达式。 函数式接口可以用来支持函数式编程,也可以用作 Lambda 表达式的类型。而 Java 8 为我们提供了一些内置的函数式接口,如 FunctionPredicate、ConsumerSupplier 等。这些接口都定义了一些常用的函数式方法,例如 Function 接口中的 apply() 方法,Predicate 接口中的 test() 方法。这些方法可以直接在 Lambda 表达式中使用。 通过使用函数式接口,我们可以更加方便地使用 Lambda 表达式来完成一些常见的操作。例如,我们可以使用 Predicate 接口来过滤集合中的元素,使用 Function 接口来对集合元素进行转换,使用 Consumer 接口来消费集合元素,使用 Supplier 接口来生产对象等等。 总之,Java 8 的函数式接口为我们提供了更加便捷的函数式编程方式。我们可以利用这些接口来定义、传递和使用 Lambda 表达式,从而简化代码,提高开发效率。同时,函数式接口也使得代码更加易读和易维护。然而,我们需要注意函数式接口的设计和使用,以避免出现意外的错误。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值