Java修炼笔记之函数式编程

2020-06-07  本文已影响0人  花醉霜寒

函数式编程简介

\color{green}{函数式编程概要}
函数式编程是一种编程范式,常见的编程范式还有命令式编程和逻辑式编程,其中命令式编程是对计算机硬件的抽象,就拿Java语言来时,变量对应存储单元、赋值语句对应获取和存储指令、表达式对应内存引用和算数运算指令、控制语句对应着跳转指令等。函数式编程是面向数学的抽象,这个函数不是计算机范畴的函数而是数学范畴的函数。关于逻辑式编程,这里不做介绍,感兴趣可查阅资料资料

\color{green}{函数式编程的特点}

\color{green}{函数式编程的优势}

Java函数式编程
JDK1.8对函数式编程进行了支持,引入了注解@FunctionalInterface来标注一个接口为函数式接口,函数式接口要求一个接口只能有一个抽象方法,但是可以有多个默认方法和静态方法。java.util.function包下提供了很多默认的函数式接口,一共有四十多个如下所示,这些接口大致可以分为四大类,分别是Function、Consumer、Supplier和Prediscate的扩展,这四个函数是整个函数式接口的基础。

\color{green}{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;
    }
}

Function接口是一个运算接口,传入一个参数,并返回一个返回值,该接口共定义了四个方法,一个抽象方法,两个默认方法和一个静态方法:

        //字符串拼接功能
        Function<String, String> function1 = str -> "hello" + str;
        
        //字符串截取
        Function<String, String> function2 = (str) -> str.substring(1);

        //返回"helloworld"
        System.out.println(function1.apply("world"));

        //返回"orld",字符串被截取
        System.out.println(function2.apply("world"));

        //返回"helloorld",先执行function2,再执行function1
        System.out.println(function1.compose(function2).apply("world"));

        //返回"elloworld",先执行function1,再执行function2
        System.out.println(function1.andThen(function2).apply("world"));

        //输出传入参数
        System.out.println(Function.identity().apply("world"));
        System.out.println(Function.identity().apply(new Object()));

输出结果

helloworld
orld
helloorld
elloworld
world
java.lang.Object@17f052a3

\color{green}{Consumer接口}
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); };
    }
}

通过下面的示例来了解一下Consumer的使用和特性

Consumer<Integer> addConsumer = param -> System.out.println(param + 100);

Consumer<Integer> consumer = param -> System.out.println(param - 50);

addConsumer.accept(50);

consumer.accept(50);

addConsumer.andThen(consumer).accept(50);

输出结果为

150
0
150
0

定义了两个Consumer分别进行+100和-50的操作并打印,最后一个输出结果我预测应该输出100,应为前面已经进行了+100的操作,但是返回结果是0,说明传入的参数被操作了两次但是没有进行赋值操作。

\color{green}{Supplier接口}
Supplier意为供应,与Consumer相反,Supplier无参数传入,但是有一个返回值,该接口比较简单,只定义了一个抽象方法get(),表示执行Supplier定义的逻辑返回一个对象。

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

通过下面的示例了解一下Supplier的定义和使用

Supplier<String> stringSupplier = () -> "hello world";

//打印hello world
System.out.println(stringSupplier.get());

定义了一个十分简单的Supplier,返回字符串“hello world”,通过get()方法可以获取。

\color{green}{Predicate接口}
Predicate意为预测,该接口接收一个参数,返回true或者false,接口定义了五个方法,其中包括一个抽象方法,三个默认方法和一个静态方法:

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

通过下面示例对Predicate的定义和使用进行简单介绍

Predicate<String> startWithPredicate = str -> str.startsWith("hello");

Predicate<String> endWithPredicate = str -> str.endsWith("world");

//true
System.out.println(startWithPredicate.test("hello world"));

//true
System.out.println(endWithPredicate.test("hello world"));

Predicate<String> andPredicate = startWithPredicate.and(endWithPredicate);

Predicate<String> orPredicate = startWithPredicate.or(endWithPredicate);

//false
System.out.println(andPredicate.test("hello java"));

//true
System.out.println(orPredicate.test("hello java"));

Predicate<String> oppPredicate = startWithPredicate.negate();

//false
System.out.println(oppPredicate.test("hello world"));

定义两个基础的Predicate,startWithPredicate判断字符串是否以“hello”开头,endWithPredicate判断字符串是否以“world”结尾,并对两个Predicate进行了"&&"、"||"以及取反操作,执行结果如上所示。

总结
本文对函数式编程进行了简单的介绍,对jdk1.8中四个最重要的函数式接口的定义和使用方法进行了详细的介绍,但是缺乏函数式编程的思想在实际项目中的应用,最近发现,虽然我最近开发的项目中通过lambda表达式使用了一些函数式接口,但是我觉得函数式接口应该是广泛使用在框架代码中的,最近在思考这个问题,后续分享。

上一篇下一篇

猜你喜欢

热点阅读