isempty函数_Spring Boot实战1-函数式编程-Lambda表达式

1 什么是函数式编程

什么是“函数式编程”?顾名思义就是用函数来编程,那什么是函数呢?函数是一种特殊的方法,它声明了如何完成任务的定义,函数体内的不修改任何共享可变的数据,没有任何副作用。Java 8中的函数(方法)是“值”的一种新的形式,可以将方法作为参数进行传递。而主要作为参数进行传递的方法主要是“Lambda表达式”和“方法引用”。

2 Lambda表达式

Lambda表达式是一种匿名函数,在函数式编程里,它也可以作为参数进行传递。

1b95e01019e5ac508c8621ec7ad6dfec.png

2.1 了解Lambda表达式

我们来看一个简单的例子,先是用老式的匿名类定义,再使用等同的Lambda表达式。

  • 匿名类:
Comparator byWeightComparator = new Comparator() {       @Override       public int compare(Person person1, Person person2) {           return person1.getWeight().compareTo(person2.getWeight());       }   };
  • Lambda表达式:
Comparator byWeightComparatorUsingLambda =                (Person person1, Person person2) -> person1.getWeight().compareTo(person2.getWeight()); 

从上面的例子我们可以看出Lambda表达式分为三个部分:

  • 左侧:Lambda参数列表(Person person1, Person person2),等同于匿名类的compare方法的参数;
  • 中间:箭头->,分开Lambda参数和Lambda体;
  • 右侧:Lambda体,person1.getWeight().compareTo(person2.getWeight()),等同于匿名类的compare方法的返回值

Lambda表达式还有以下的规则:

  • Lambda表达式可以有0到多个参数,如:
    • () -> {}
    • (Integer i) -> "wyf"
    • (Integer i, Integer j) -> { return "wyf";}
  • Java编译器有类型推断(Type inference)的能力,Lambda参数的类型可以省略,如:
(person1, person2) -> person1.getWeight().compareTo(person2.getWeight());
  • 若Lambda参数只有一个的话,可省略圆括号,如:a -> a+1。
  • Lambda体需要用花括号围绕,如果体内只有一句表达式的话可省略。
  • Lambda体中使用return的话,需要使用花括号,如:() -> { return "wyf";}。

2.2 Lambda表达式作为参数

Lambda表达式可以像参数一样传递给方法,看如下的示例:

List people = Arrays.asList(new Person("wyf", Gender.MALE, 100),                                new Person("www", Gender.FEMALE, 80),                                new Person("foo", Gender.FEMALE, 90));people.sort((p1, p2) -> p1.getWeight().compareTo(p2.getWeight())); //1people.forEach(person -> System.out.println(person.getName() + "的体重:" + person.getWeight())); //2
  1. Lambada表达式作为sort方法的参数,是Comparator函数接口的实现。
  2. Lambada表达式作为forEach方法的参数,是Consumer函数接口的实现。

Person类的定义如下:

public class Person {    private String name; //名字    private Gender gender; //性别    private Integer weight = 0; //体重    //无参构造    public Person() {        super();    }    //一个参数构造    public Person(String name) {        this.name = name;    }    //两个参数构造    public Person(String name, Gender gender) {        this.name = name;        this.gender = gender;    }    //全参构造    public Person(String name, Gender gender, Integer weight) {        this.name = name;        this.gender = gender;        this.weight = weight;    }...省略getter、setter    @Override    public String toString() {        return "名字是:" + name + "性别是:" + gender + "体重是:" + weight;    }}

Gender的定义如下:

public enum  Gender {    MALE, FEMALE}

3 函数接口

上面的例子里面我们涉及到了很多函数接口,如:

  • Function
  • Consumer
  • Comparator

它们都属于函数接口,都标记了@FunctionalInterface注解:如:

@FunctionalInterfacepublic interface Function...@FunctionalInterfacepublic interface Consumer ...@FunctionalInterfacepublic interface Comparator...

任意只有一个抽象方法的接口都是函数接口(Functional Interface),这类接口的实现都可以使用Lambda表达式(或方法引用)。在定义中说到函数接口只有一个抽象方法,而函数接口里其实还有其它很多的方法,这些方法将被定义忽略,这些方法分别归类为:

  • 静态方法:和接口有关的工具助手方法。使用static关键字,提供实现。
  • 默认方法:添加新的功能方法到已有的接口,老的代码中使用了该接口其它方法的代码不会受到影响。使用default关键字,提供实现。

这也意味着从Java 8开始,接口内不仅仅只有抽象方法,还可以有静态方法和默认方法。只要符合定义即使没有标记@FunctionalInterface也是函数接口,当然如果不符合函数接口的定义即使标记了@FunctionalInterface,编译器就会报错,这就是@FunctionalInterface的作用。

函数接口主要在java.util.function包下,主要分成下面几类:

  • Predicate:有输入,输出布尔值的函数;
  • Function:有输入有输出的函数;
  • Consumer:有输入无输出的函数;
  • Supplier:无输入有输出的函数;
  • Operator:输入输出为相同类型的函数;

3.1 Predicate-断言

Predicate的源码定义为:

@FunctionalInterfacepublic interface Predicate {    boolean test(T t);}

Lambda表达式即为test方法的实现,从test方法的定义我们可以看出,test方法接受任意类型的参数T,返回值boolean,我们可以用下面的表达式定义:

Predicate emptyPredicate = (String s) -> s.isEmpty()

根据类型推断可缩写为:Predicate emptyPredicate = s -> s.isEmpty()。

使用当前Predicate定义,可通过test方法执行:

Predicate emptyPredicate = s -> s.isEmpty();System.out.println(emptyPredicate.test("wyf"));

输出的emptyPredicate.test("wyf")返回值为false。

3.1.1 组合Predicate

Predicate接口包含:negate、and、和or方法,可以重用已有的Predicate并组成复杂的Predicate。

  • negate:已有Predicate的否定
Predicate nonEmptyPredicate = emptyPredicate.negate();
  • and:相当于逻辑运算的&&。
Predicate greaterThan0 = i -> i > 0;Predicate lessThan100 = i -> i < 100;Predicate between0And100 = greaterThan0.and(lessThan100);

只有在i > 0且i < 100的情况下,test方法的返回值才为true。

  • or:相当于逻辑运算的||。
Predicate greateThan0OrLessThan100 = greaterThan0.or(lessThan100);

在i > 0 或i < 100时,test方法的返回值是true。

3.1.2 原始数据类型Predicate

Java会自动将包装类型拆包成原始数据类型,但这意味着性能的损失,所以当数据为原始数据类型时候,Java 8提供了一些特殊的Predicate。

  • IntPredicate:当入参是int型时;

Predicate greaterThan0 = i -> i > 0;

可修改为:

IntPredicate intGreaterThan0 = i -> i > 0;

  • DoublePredicate:当入参为double型时;
  • LongPredicate:当入参为long型时;

3.1.3 两个参数的Predicate

Java还给我们提供了两个入参的Predicate叫做 - BiPredicate

@FunctionalInterfacepublic interface BiPredicate {    boolean test(T t, U u);}

Test方法接受两个入参,类型分别为T和U,我们可以定义为:

BiPredicate isLongThanGivenLength = (str, len) -> str.length() > len;

第一个入参T为类型为String(str),第二个入参U类型为Integer(len)。

3.2 Function-函数

Function的源码定义为:

@FunctionalInterfacepublic interface Function {    R apply(T t);}

同样Lambda表达式是apply方法的实现,apply方法接受任意类型的参数T,返回值类型为R,我们可以用下面的表达式定义:

Function lengthFunction = str -> str.length();

入参T类型为String(str),返回值R类型为Integer(str.length()),使用当前Function定义,可通过apply方法执行:

Function lengthFunction = str -> str.length();System.out.println(lengthFunction.apply("wyf"));

输出的lengthFunction.apply("wyf")返回值为3。

3.2.1 组合Function

Function接口函数也为我们提供了andThen和compose方法组合已有的Function,组合的返回值仍为Function。先定义两个将被组合的Function:

Function plusFunction = x -> x + x ;Function multipleFunction = x -> x * x;
  • andThen:新的Function是组合第一个函数的返回值作为第二个函数的输入。
Function andThenFunction  = plusFunction.andThen(multipleFunction);System.out.println(andThenFunction.apply(2));

执行时,plusFunction先执行,返回值作为multipleFunction的入参再执行,结果为16。

  • compose:新的Function是组合第二个函数的返回值作为第一个函数的输入。
Function composeFunction = plusFunction.compose(multipleFunction);System.out.println(composeFunction.apply(2));

执行时,multipleFunction先执行,返回值作为plusFunction的入参再执行,结果为8。

3.2.2 原始数据类型Function

像Predicate一样,Function也有原始数据类型的Function,主要有三类:

第一类入参固化为函数接口,返回值类型R,仍需在范型中定义:

  • IntFunction:入参为int。上面的plusFunction可修改为:
IntFunction intPlusFunction = x -> x + x ;
  • LongFunction:入参为long。
  • DoubleFunction:入参为double。

第二类是返回值固化为函数接口,入参类型T仍需在范型中定义:

  • ToIntFunction:返回值类型为int。
ToIntFunction toIntFunction = str -> str.length();System.out.println(toIntFunction.applyAsInt("www"));
  • ToLongFunction:返回值类型为long。
  • ToDoubleFunction:返回值类型为double。

第三类是入参和返回值都固化为函数接口:

  • IntToLongFunction:入参为int,返回值为long。
IntToLongFunction intToLongFunction = i -> i;System.out.println(intToLongFunction.applyAsLong(1));
  • IntToDoubleFunction:入参为int,返回值为double。
  • LongToIntFunction:入参为long,返回值为int。
  • LongToDoubleFunction:入参为long,返回值为double。
  • DoubleToIntFunction:入参为double,返回值为int。
  • DoubleToLongFunction:入参为double,返回值为long。

3.2.3 两个入参的Function

Function是一个入参T和一个返回值R,Java还提供了BiFunction,源码定义为:

@FunctionalInterfacepublic interface BiFunction {    R apply(T t, U u);}

Apply方法接受两个参数T和U,返回值为R,我们可以用下面的表达式定义:

 BiFunction totalLengthBiFunction = (str1, str2) -> str1.length() + str2.length(); System.out.println(totalLengthBiFunction.apply("wyf","www"));

apply方法的第一个入参T是String(str1),第二个入参U是String(str2),返回值是两个字符串长度之和,执行apply方法输出为6.

同样BiFunction也有很多的原始数据类型函数:

  • ToIntBiFunction:返回值为int型。
  • ToLongBiFunction:返回值为long型。
  • ToDoubleBiFunction:返回值为double型。

3.3 Consumer-消费者

Consumer顾名思义消费而不生产,Consumer的源码定义如下:

@FunctionalInterfacepublic interface Consumer {    void accept(T t);}

从accept方法的定义中可以看出,accept方法接受一个参数T,没有返回值,我们可以用下面的表达式定义:

Consumer helloConsumer = str -> System.out.println("Hello " + str + "!");helloConsumer.accept("wyf");

accept方法接受的参数T是String(str),没有返回值。

Consumer同样也有原始数据类型接口:

  • IntConsumer:入参为int。
  • LongConsumer:入参为long。
  • DoubleConsumer:入参为double。

Consumer也有两个参数的BiConsumer接口,包含:

  • ObjIntConsumer:第一个入参为任意类型T,第二个入参为int。
  • ObjLongConsumer第一个入参为任意类型T,第二个入参为long。
  • ObjDoubleConsumer:第一个入参为任意类型T,第二个入参为double。

3.4 Supplier-提供者

Supplier顾名思义是只生产不消费,Supplier的源码定义如下:

@FunctionalInterfacepublic interface Supplier {    T get();}

Get方法不接受参数,返回值为T,我们可以用下面的表达式定义:

Supplier systemTime = () -> System.currentTimeMillis();System.out.println(systemTime.get());

get方法没有入参,返回值是Long型,输出当前系统事件。

Consumer同样也有原始数据类型接口:

  • IntSupplier:返回值是int型。
  • LongSupplier:返回值是long型。
  • DoubleSupplier:返回值是double型。
  • BooleanSupplier:返回值是boolean型。

3.5 Operator-操作者

Operator其实是一种特殊的Function,它的输入和返回值都是同一种类型。

3.5.1 UnaryOperator

UnaryOperator继承了Function接口,如定义:

@FunctionalInterfacepublic interface UnaryOperator extends Function

UnaryOperator接受一个入参T,返回值也是类型T。

同样UnaryOperator也有原始数据类型的函数接口:

  • IntUnaryOperator:入参返回值都为int型。
  • LongUnaryOperator:入参返回值都为long型。
  • DoubleUnaryOperator:入参返回值都为double型。

3.5.2 BinaryOperator

BinaryOperator继承了Function接口,如定义:

@FunctionalInterfacepublic interface BinaryOperator extends BiFunction

BinaryOperator接受的两个入参类型都为T,返回值类型也是T。

同样BinaryOperator也有原始数据类型的函数接口:

  • IntBinaryOperator:两个入参和一个返回值都是int型。
  • LongBinaryOperator:两个入参和一个返回值都是long型。
  • DoubleBinaryOperator:两个入参和一个返回值都是double型。

3.6 Comparator

Comparator是我们经常用来比较排序所用的一个函数接口,它的定义:

@FunctionalInterfacepublic interface Comparator {      int compare(T o1, T o2);}

若o1小于o2,返回负数;若o1等于o2,返回0;若o1大于o2,返回正数。我们可以用Lambda表达式来定义:

Comparator byWeightComparatorUsingLambda =        (person1, person2) -> person1.getWeight().compareTo(person2.getWeight());

Comparator函数接口还为排序提供了comparing的静态方法,它接受一个Function来获取处理数据的排序key,上面的语句可以简写成:

Comparator byWeightComparatorUsingStatic =Comparator.comparing(Person::getWeight);

3.7 自定义函数接口

函数接口的定义主要是看入参和返回值,前面我们有一个入参的Function,有两个入参的BiFunction,我们现在自定义一个有三个入参的TriFunction。

@FunctionalInterface //1public interface TriFunction {    R apply(T t, U u, W w); //2}
  1. @FunctionalInterface标记为函数接口
  2. Apply方法接受三个入参T(t)、U(u)和W(w),返回值为R。

我们可以通过如下Lambda表达式调用:

TriFunction lengthTriFuntion =                (str1, str2, str3) -> str1.length() + str2.length() + str3.length();System.out.println(lengthTriFuntion.apply("wyf", "www", "foo"));

第一个入参String(str1),第二个入参String(str2),第三个入参String(str3)以及返回值类型为Integer,计算输出三个字符串的长度之和,输出结果为9。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值