当前位置:   article > 正文

【转】Java8 函数式编程详解

java8 函数

1.Java函数式编程的语法:

使用Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept,这个方法只有输入而无输出也就是说这个方法无返回值。
现在我们要定义一个Consumer接口的实例化对象,传统的方式是这样定义的:

public static void main(String[] args) {
	//JDK1.8版本之前的做法
	Consumer<Integer> con = new Consumer<Integer>() {
		@Override
		public void accept(Integer t) {
			System.out.println(t);
		}
	};
	//调用方法
	con.accept(3);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这里可以打印出3.
上面是JDK1.8以前的做法,在1.8以后引入函数式的编程可以这样写:

public static void main(String[] args) {
	// 1. Consumer只有一个入参,没有出参
	// 第一种写法(有且只有一个入参)
	Consumer<Integer> con = (param) -> {
		System.out.println(param);
	};
	// 第二种写法
	Consumer<Integer> con1 = (param) -> System.out.println(param);
	// 第三种写法
	Consumer<Integer> con2 = System.out::print;
	Consumer<Integer> con3 = (param) -> {
		System.out.println(param * param);
	};
	Consumer<Integer> con4 = (param) -> {
		System.out.println(param + param + 1);
	};
	// 调用
	con.accept(1);
	con1.accept(2);
	con2.accept(3);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

上面的con、con1、con2这个Consumer对象的引用照样可以打印出3.

  • 在上面的第二种写法是第一种写法的精简,但是只有在函数题中只有一条语句时才可以这么使用,做进一步的简化。
  • 在上面的第三种写法是对第一、二种写法的进一步精简,由于第三种写法只是进行打印,调用了System.out中的println静态方法对输入参数直接进行打印。它表示的意思就是针对输入的参数将其调用System.out中的静态方法println进行打印。
  • 上面已说明,函数式编程接口都只有一个抽象方法,因此在采用这种写法时,编译器会将这段函数编译后当作该抽象方法的实现。
    如果接口有多个抽象方法,编译器就不知道这段函数应该是实现哪个方法的了。 因此,= 后面的函数体我们就可以看成是accept函数的实现。
  • 输入:-> 前面的部分,即被()包围的部分。此处只有一个输入参数,实际上输入是可以有多个的(Consumer只有一个入参),如两个参数时写法:(a, b);当然也可以没有输入,此时直接就可以是()。
  • 函数体:->后面的部分,即被{}包围的部分;可以是一段代码。
  • 输出:函数式编程可以没有返回值,也可以有返回值。如果有返回值时,需要代码段的最后一句通过return的方式返回对应的值。但是Consumer这个函数式接口不能有具体的返回值。
  • Java 8 中我们可以通过 :: 关键字来访问类的构造方法,对象方法,静态方法。

好了,到这一步就可以感受到函数式编程的强大能力。
通过最后一段代码,我们可以简单的理解函数式编程,Consumer接口直接就可以当成一个函数了,这个函数接收一个输入参数,然后针对这个输入进行处理;当然其本质上仍旧是一个对象,但我们已经省去了诸如老方式中的对象定义过程,直接使用一段代码来给函数式接口对象赋值。
而且最为关键的是,这个函数式对象因为本质上仍旧是一个对象,因此可以做为其它方法的参数或者返回值,可以与原有的代码实现无缝集成!

2. Java函数式接口

常用的接口有:

java.util.function.Consumer;
java.util.function.Function;
java.util.function.Predicate;
  • 1
  • 2
  • 3

2.1 Consumer

Consumer是一个函数式编程接口; 顾名思义,Consumer的意思就是消费,即针对某个东西我们来使用它,因此它包含有一个有输入而无输出(无返回值)的accept接口方法;
除accept方法,它还包含有andThen这个方法;
JDK源码定义如下:

default Consumer<T> andThen(Consumer<? super T> after) {
	Objects.requireNonNull(after);
	return (T t) -> { accept(t); after.accept(t); };
}
  • 1
  • 2
  • 3
  • 4

andThen这个方法是作用是:指定在调用当前Consumer后是否还要调用其它的Consumer;

    public static void main(String[] args) {
        //定义第一个Consumer
        Consumer<Integer> consumer1 = (param) -> {
            System.out.println(param);
        };
        //定义第二个Consumer
        Consumer<Integer> consumer2 = (param) -> {
            System.out.println(param * param);
        };
        //consumer1可以连续的调用自己
        consumer1.andThen(consumer1).andThen(consumer1).accept(3);
        //打印出 3 3 3
        //consumer1可以调用自己后调用consumer2
        consumer1.andThen(consumer1).andThen(consumer2).accept(3);
        //打印出3 3 9
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

注意:当一个Consumer接口调用另外一个Consumer对象时两个Counsumer对象的泛型必须一致。

2.2 Function

Function也是一个函数式编程接口;它代表的含义是“函数”,它是个接受一个参数并生成结果的函数,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出;
除apply方法外,它还有compose与andThen及indentity三个方法,其使用见下述示例;

apply的用法:

    public static void main(String[] args) {
        Function<Integer, Integer> fun = res -> res + 1;
        Integer i = fun.apply(2);
        System.out.println(i);
    }
  • 1
  • 2
  • 3
  • 4
  • 5

上面打印出2。
Function编程接口有两个泛型Function<T, R> T表示:函数的输入类型,R表示:函数的输出类型。

compose的用法:

JDK源码定义:

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
  • 1
  • 2
  • 3
  • 4
    public static void main(String[] args) {
        Function<Integer, Integer> fun = res -> res + 1;
        Function<Integer, Integer> fun1 = res -> res * 10;
        Integer i = (Integer) fun.compose(fun1).apply(2);
        System.out.println(i);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上面打印出21;
上面表示了 fun1 接收到 2 后先计算,然后将 fun1 计算的结果再交给 fun 再计算。

andThen方法的用法:

JDK源码定义:

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
  • 1
  • 2
  • 3
  • 4
    public static void main(String[] args) {
        Function<Integer, Integer> fun = res -> res + 1;
        Function<Integer, Integer> fun1 = res -> res * 10;
        Integer i = (Integer) fun.andThen(fun1).apply(3);
        System.out.println(i);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上面打印出40
上面表示了 fun 先计算得到4,然后将计算结果4再给 fun1 计算得到40;

indentity方法的用法:

JDK源码定义:

    static <T> Function<T, T> identity() {
        return t -> t;
    }
  • 1
  • 2
  • 3
    public static void main(String[] args) {
        System.out.println(Function.identity().apply("总分"));
    }
  • 1
  • 2
  • 3

上面打印出“总分“;就是说传入什么参数输出什么参数。

2.3 Predicate

Predicate为函数式接口,predicate的中文意思是“断定”,即判断的意思,判断某个东西是否满足某种条件; 因此它包含test方法,根据输入值来做逻辑判断,其结果为True或者False。

test方法的用法:

JDK源码定义:

boolean test(T t);
  • 1
    public static void main(String[] args) {
        Predicate<String> pre = res -> res.equals("1234");
        boolean rest = pre.test("1234");
        System.out.println(rest);
    }
  • 1
  • 2
  • 3
  • 4
  • 5

上面打印出true。

and方法的用法:

JDK源码定义:

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
  • 1
  • 2
  • 3
  • 4
    public static void main(String[] args) {
        Predicate<String> pre = res -> res.equals("1234");
        Predicate<String> pre1 = res -> res.equals("1234");
        Predicate<String> pre3 = res -> res.equals("1234");
        boolean rest = pre.and(pre1).test("1234");
        boolean rest1 = pre.and(pre1).test("12341");
        System.out.println(rest);
        System.out.println(rest1);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上面先打印出true,后打印出false

根据源码,"1234"先和pre比较,如果为true,再和pre1比较。不为true直接返回false,不再和pre1比较。当“1234”和pre比较后为true,再和pre1比较结果为true,所以最终返回true。

negate()的用法:

JDK源码定义

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
  • 1
  • 2
  • 3
    public static void main(String[] args) {
        Predicate<String> pre = res -> res.equals("1234");
        boolean rest = pre.negate().test("1234");
        System.out.println(rest);
    }
  • 1
  • 2
  • 3
  • 4
  • 5

上面打印出false,上面的意思是如果比较的结果是true,那么返回false,如果为false,就返回true。

or的方法的用法:

JDK源码定义:

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
  • 1
  • 2
  • 3
  • 4
    public static void main(String[] args) {
        Predicate<String> pre = res -> res.equals("1234");
        Predicate<String> pre1 = res -> res.equals("12341");
        boolean rest = pre.or(pre1).test("12341");
        System.out.println(rest);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上面打印出true;or方法的意思是:只要一个为true就返回true。

isEqual方法用法:

JDK源码定义:

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
    public static void main(String[] args) {
        System.out.println(Predicate.isEqual("12345").test("12345"));
    }
  • 1
  • 2
  • 3

上面打印出true;isEqual里的参数是要比较的目标对象。

3.函数式编程接口的使用

通过Stream以及Optional两个类,可以进一步利用函数式接口来简化代码。

3.1 Stream

Stream可以对多个元素进行一系列的操作,也可以支持对某些操作进行并发处理。

3.1.1 方法
Stream对象提供多个非常有用的方法,这些方法可以分成两类:

  • 中间操作:将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
  • 终端操作:产生的是一个结果或者其它的复合操作;如count或者forEach操作。

所有中间操作:
在这里插入图片描述
所有的终端操作:
在这里插入图片描述

3.2 Optional

Optional用于简化Java中对空值的判断处理,以防止出现各种空指针异常。
Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。

3.2.1 方法
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/289824
推荐阅读
相关标签
  

闽ICP备14008679号