Rxjava和lambda语法

简介: Rxjava和lambda语法

本文大部分代码基于lambdaexpressions写在前面的话


大概是在一年前知道RxJava项目,于是兴致勃勃的上网去搜索各种关于RxJava的各种教程。当看到类似下面的代码时,总感觉跟平常写的代码有些不一样,感觉除了Builder模式一般不会出现这么多的函数串联调用。但是又不是Builder模式实在是有点费解。仔细看有Func1 Action1这样的接口类,实在是费解如此命名下的类的含义,为何如此大名鼎鼎的框架会违背Java命名规范?


image.png

之后由于时间精力有限,也就没有再深入学习RxJava。但是RxJava的一些疑问点还是一直存留在脑海中。不明白的始终还是不明白。直到有一天看到了一篇关于Lambda的文章。才豁然开朗,然后再对RxJava二进宫。果然事半功倍,很快就掌握了RxJava基础。所以Lambda是RxJava的基础是成立的。


那么什么是Lambda表达式呢?


我们在写Android程序或者GUI程序时,按钮的点击事件代码是信手拈来


image.png

image.png

Java8加入了对Lambda表达式的支持,上面代码的Lambda表达式为

image.png

Lambda表达式是多么的简洁原本七行的代码用两行代码就轻轻松松搞定.(View v)可以将类型省略掉,因为v类型可以自动推倒

image.png

上述Lamda是Android内置的函数,那么我们如何编写属于自己的Lambda表达式下面我们将通过一个实例来一步步讲解

image.png

现在我们有一个Person的集合List roster。我们要在roster中打印出符合某些条件的Person的信息有如下场景


1.打印出年纪大于18岁的人的信息,我们可能会编写如下代码

image.png

2.打印出18到25岁的人的信息,然后我们添加一个方法

//函数定义
public static void printPersonsWithinAgeRange(List<Person> roster, int low, int high){
    for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}
//函数调用
List<Person> roster = ...;
printPersonsWithinAgeRange(roster,18,25);

3.通过前面两个例子我们发现如果需要查找符合新的条件的人的信息时就需要添加新的方法。于是我们决定使用接口来做判断

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
        for (Person p : roster) {
            if (tester.test(p)) {
                p.printPerson();
            }
    }
}
//定义接口
interface CheckPerson {
    boolean test(Person p);
}   
//寻找18岁到25之间的男性
class CheckPersonEligibleForSelectiveService implements CheckPerson {
    public boolean test(Person p) {
        return p.gender == Person.Sex.MALE &&
            p.getAge() >= 18 &&
            p.getAge() <= 25;
     }
}
//具体调用
printPersons(roster, new CheckPersonEligibleForSelectiveService());
————————————————

4. 使用匿名内部类

printPersons(roster, new CheckPersonEligibleForSelectiveService());
#等价于
printPersons(roster,new CheckPerson() {
    public boolean test(Person p) {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }
});

5.使用Lambda表达式

printPersons(
    roster,
    (Person p) -> p.getGender() == Person.Sex.MALE
                  && p.getAge() >= 18
                  && p.getAge() <= 25
);

6.使用更通用的Predicate


假设我们现在有个Teacher类,我们也需要根据一些条件打印Teacher的一些信息。我们可能会定义一个接口

//定义接口
interface CheckTeacher {
    boolean test(Teacher t);
}

其实我们发现CheckTeacher和CheckPerson的定义其实完全一样,完全可以用泛型定义成一个接口。所以Java类库考虑到这点定义了Predicate

interface Predicate<T> {
    boolean test(T t);
}

于是函数定义变成了

public static void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
             p.printPerson();
        }
    }
}

函数调用依然不变

printPersonsWithPredicate(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);


7.使用Comsumer,Comsumer源码如下

@FunctionalInterface
public interface Consumer<T> {
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

接下来我们重新定义printPersons方法

//注意我们这里不在是printPersons 因为通过使用Consume我们可以在查找到符合条件的对象后我们可以自定义如何处理这些对象
//我们不仅仅局限于打印出这些人的信息这样一个动作了
public static void processPersons(List<Person> roster,Predicate<Person> tester, Consumer<Person> block) {
    for (Person p : roster) {
        if (tester.test(p)) {
            block.accept(p);
        }
    }
}
//调用如下
List<Person> roster = ...;
processPerson(roster,//第一个参数
              (Person p)->{p.getAge()>18;},//第二个参数
              (Person p)->{//第三个参数
                            p.printPerson();
                            System.out.println("还可以做任何额外")
                            }
            );

8.上面的例子都是打印Person的全部信息,那如果我只想打印出符合条件的人的Email该怎么办,有两种办法


第一种 直接将7中的最后一个参数改成 (Person p)->System.out.println(p.getEmailAddress())


第二种 在block.accept(p) 想办法将p 变成String

if (tester.test(p)) {
   //在这里我们应该想办法获取到p的Email
    block.accept(p);
 }
 将这些改成
 if (tester.test(p)) {
    String email = p.getEmailAddress();
    block.accept(email);
 }
 但是如果都是这样写的话那么代码的侵入性太强了,Java提供了Function<T,R>接口用来转换,跟RxJava的map方法是不是有点像
 完整定义如下
 public static <X, Y> void processElements(Iterable<X> source,Predicate<X> tester,Function <X, Y> mapper,
                                                Consumer<Y> block) {
    for (X p : source) {
        if (tester.test(p)) {
            Y data = mapper.apply(p);
            block.accept(data);
        }
     }
} 
调用如下
processElements(roster,//第一个参数
                (Person p)->p.getAge()>18,//第二个参数
                (Persion p)->p.getEmailAddress(),//第三个参数,此时已经将Person转换成String了
                (String s)->System.out.println(s),//第四个参数,s的类型已经转换成String了
                );

9.进一步精简。上述我们发现函数式编程会导致有很多匿名内部对象作为参数,代码可读性不强,容易出错误。Java通过Stream解决这一个问题

roster
    .stream()
    .filter(p->p.getAge()>18)//等价于8中的第二个参数
    .map(p->p.getEmailAddress())//等价于8中的第三个参数
    .forEach(email->System.out.println(email));//等价于8中的第四个参数

我们最终看一个RxJava的例子,读者可以对比例子9

Integer[] list = {1,2,3,4,5};
Observable
    .from(list)
    .filter(integer->integer%2==0)//挑选出偶数
    .map(integer -> "number is"+integer)//转换成String
    .subscribe(s->System.out.println(s));//相当于forEach(s->System.out.println(s));
    //forEach是同步的 subscribe是异步的


总结



第一次写文章,不对之处望指正

相关文章
|
1月前
|
算法 编译器 C++
【C++】—— c++11新特性之 lambda
【C++】—— c++11新特性之 lambda
|
8月前
|
API Kotlin
Kotlin中扩展函数、infix关键字、apply函数和DSL的详解
Kotlin中扩展函数、infix关键字、apply函数和DSL的详解
77 0
|
9月前
|
Java API 开发者
Lambda表达式:简介、语法和用法
Lambda表达式:简介、语法和用法
14882 8
Lambda表达式:简介、语法和用法
|
5月前
|
Python
Python的函数式编程指南:Lambda、Map和Filter的应用
Python是一门多范式的编程语言,允许你以不同的方式编写代码,包括面向对象编程、过程式编程和函数式编程。函数式编程是一种强大的编程范式,它强调不可变性、纯函数和高阶函数的使用。本文将引导你了解Python中函数式编程的一些核心概念和技巧,特别是Lambda、Map和Filter的应用。
|
5月前
|
Scala
172 Scala 高阶函数例子
172 Scala 高阶函数例子
19 0
|
编译器
java--Lambda(2)表达式语法
最基本的 Lambda 表达式,它由三部分组成具体格式是这样子的:参数 -> 具体实现;Lambda表达式格式:()->{}是一段带有输入参数的可执行语句块。可选类型声明: 不需要声明参数类型,编译器可以自动识别参数类型和参数值。可选的参数圆括号: 一个参数可以不用定义圆括号,但多个参数需要定义圆括号;可选的大括号: 如果函数主体只包含一个语句,就不需要使用大括号;可选的返回关键字: 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值。
java--Lambda(2)表达式语法
|
JavaScript 前端开发 Java
java8新特性,Lambda 表达式
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号 可选的大括号:如果主体包含了一个语句,就不需要使用大括号 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值 3.Lambda表达式的五种使用场景 第一种场景:无参,无返回值类型
106 0
java8新特性,Lambda 表达式
|
Java 编译器
Java 8 新特性:Lambda 表达式之方法引用(Lambda 表达式补充版)
Java 8 新特性:Lambda 表达式之方法引用(Lambda 表达式补充版)
199 0
Java 8 新特性:Lambda 表达式之方法引用(Lambda 表达式补充版)
|
存储 自然语言处理 安全
Java 8 新特性:Lambda 表达式的作用域(Lambda 表达式补充版)
Java 8 新特性:Lambda 表达式的作用域(Lambda 表达式补充版)
553 0
|
自然语言处理 JavaScript 前端开发
Java 8 新特性:Lambda 表达式
Java 8 新特性:Lambda 表达式
208 0
Java 8 新特性:Lambda 表达式