JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

一天一个编程小知识之Java中的函数式编程

wys521 2025-02-03 15:42:11 精选教程 20 ℃ 0 评论

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。

为什么java中可以使用函数式编程

Java本身是面向对象的程序设计语言,要想在Java中使用函数式编程的特性,首先需要支持函数式编程。编程语言中需要有结构来表示函数是支持函数式编程的一大特征。在Java中使用接口来表示函数,Python中有map和reduce等。

在java中函数的定义

函数式编程可以提高我们的开发效率(什么是函数式编程),那么在Java中怎么定义和使用这样一个特性呢?

上面说明了Java中是通过接口定义函数的,所以如果接口中添加了@FunctionalInterface注解,则这个接口是可以用做函数式编程的。

当然并不是所有接口都可以添加这个注解的,@FunctionalInterface注解有几个限制条件,分别是:

  • 接口有且仅有一个抽象方法
  • 允许定义静态方法
  • 允许定义默认方法

直到Java8,Java才提供了内置标准API来表示函数,也就是java.util.function包。Function<T, R>表示接受一个参数的函数,输入类型为T,输出类型为R

下面以java.util.function包下的Function接口的源码为例:

@FunctionalInterface
public interface Function<T, R> {
    // 有且仅有一个抽象方法
    R apply(T t);
    // 默认方法1
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    // 默认方法2
    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;
    }
}

除了 Function ,Java 标准库还提供了几种特殊类型的函数:

  • Consumer:接受一个输入,没有输出。抽象方法为 void accept(T t)。
  • Supplier:没有输入,一个输出。抽象方法为 T get()。
  • Predicate:接受一个输入,输出为 boolean 类型。抽象方法为 boolean test(T t)。
  • UnaryOperator:接受一个输入,输出的类型与输入相同,相当于 Function<T, T>。
  • BinaryOperator:接受两个类型相同的输入,输出的类型与输入相同,相当于 BiFunction<T,T,T>。
  • BiPredicate<T, U>:接受两个输入,输出为 boolean 类型。抽象方法为 boolean test(T t, U u)。
另外如果一个接口满足上面的`@FunctionalInterface`注解的要求可以不需要显示的添加`@FunctionalInterface`注解。
不过通常还是主动添加`@FunctionalInterface`注解,添加注解后如果在当前接口再添加抽象方法时,编辑器会提示不允许添加。

如何自定义函数接口

根据@FunctionalInterface注解的要求可以实现自定义函数接口。

// 自定义函数接口
@FunctionalInterface
public interface CustomAddFunc<T, R> {
  R add(T t);
}

在Java中函数的使用

因为函数式编程的特点是允许把函数本身作为参数传入另一个函数,所以方法的参数含有上面Java标准库或者自定义的函数类型参数时,就是定义了一个函数式编程的方法了。

带函数参数的方法定义

下面以java.util.stream包下的Stream接口部分源码为例:

// Stream接口部分源码
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    Stream<T> filter(Predicate<? super T> predicate);
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
}

filtermap方法都有一个Java标准库中特殊类型的函数参数。

`Function`接受一个参数的函数,输入类型为 T,输出类型为 R。
`Predicate`接受一个输入,输出为 boolean 类型。

带函数参数的方法使用

// 通过匿名内部类的方式实现函数接口
int sum = Arrays.stream(new int[]{1, 2, 3}).map(new IntUnaryOperator() {
    @Override
    public int applyAsInt(int operand) {
        return operand + 1;
    }
}).sum();
System.out.println(sum); // 输出 9

优雅的使用带函数参数的方法

Java8提供了lambda表达式,是创建匿名内部类的语法糖(syntax sugar)。所以上面的使用方式可以优化成更短的代码

int l = Arrays.stream(new int[]{1, 2, 3}).map(v -> v + 1).sum();
System.out.println(l);

结合lambda表达式使用函数式编程可以很大程度地减少不必要的代码,达到提高开发效率的目的,而且在表达上也更清晰。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表