00:00
通过对聚合了完全一致的所谓的data stream API,其实就是要基于data stream这样一个数据结构数据流去调用不同的方法,经过转换处理之后得到的还是data strip,当然中间可能会需要在聚合之前要做一些K按键分组、分区进行这样的一些操作,但是最终转换之后。得到的还是。所以在这个过程当中,我们会发现data stream去调用方法的处理操作也是非常类似的,每次都是当前的方法名就表示了我们想要做的转换计算操作,而这个方法呢,往往都需要传入一个参数。这个参数只需要实现一个接口,那具体的这个接口名称我们就会发现了,跟当前的算子名称是完全一致的。比如像一开始我们基于环境,当然不是基于data stream了啊,是基于环境调source方法的时候,其实定义的就是我们最初获取生成data stream的基本的第一个算子S算,S算子里面要传入的就是一个S方式接口。
01:27
而后面基于这STEM进行转换的时候呢,所有的transform操作map算子里边要传入的参数实现就是map方式接口,Reduce要实现的就是reduce function接口,Filter实现的就是filter function接口。所有的这些接口我们就会发现都是可以自行定义,把它们定义出来,然后去实现我们想要的功能的。所以本身flink的编程是非常灵活的,所有的底层逻辑、业务逻辑我们都可以自己调用通用接口去实现。
02:02
就是我们所谓的用户自定义函数udf,它其实本身就是flink底层的编程风格。那仔细观察源码,我们也可以发现,比如这里的reduce function。里边只有单一的抽象方法,唯一的抽象方法我们知道这就是所谓的单一抽象方法接口。Sam散。哎,那我们知道对于这样的三接口呢,JAVA8其实是有相关的优化和约定的。Java巴所新增的拉姆达表达式本身想要实现的,那就是这样一个单一抽象接口的实现类啊,那所以对于这样的接口,我们实现udf的时候呢,可以去直接。定义一个类去实现它,然后传入相应的对象。也可以直接在参数位置传入一个匿名类,当然另外也可以传入一个拉姆达表达式,所以接下来我们就完整的再做一个梳理,其实所有的这些方法在前面的讲解过程当中我们都已经用过了,所以这里就简单的做一个回顾。首先我们最简单的方式就是。
03:19
直接定义一个类去实现这个接口啊,这就是所谓的函数类,因为我们直接追到源码里看的话会发现啊,所有的对应的接口底层要实现的都是继承字方式那方式,这里边它没有任何的。抽象方法的定义,那当然了,它是空的,所以我们真正定义操作的这个接口才可以成为单一抽象方法接口啊,而这个方式指带的很明确,我们其实想要实现的是一个函数操作,那它本身是一个接口,所以我们这里边。实现出来的就是一个函数类的对象啊,那对于大部分操作而言,我们最简单的方式啊,就是直接把。
04:08
使用一个类去实现这个接口就可以了,比如说这里边我们已经类似定义过的filter function啊,我们可以定义按照当前。每一个事件里边的user,哎,去做一个filter,这是可以的,或者我们也可以像当前定义的这个filter function一样,判断有没有包含某一个关键字,比如说我要选取的就是当前URL里边有home这样一个关键字的事件,就全部筛选出来,诶,这完全可以这样去做定义,那我们当前是使用了自定义的类,然后实现了filter function接口需要带上。泛型参数就是当前的数据类型,这是最常见的一个用法。当然了,我们也可以在代码里边直接不要单独去声明对应的类,直接代码里边用匿名类来实现接口,实现的过程是一样的。
05:01
那这种方法呢,其实有一个非常明显的好处,就是首先当前的类型都是清清楚楚,明明白白的啊,弗林可以完整的把类型进行解析,我们不需要在前面调用了各种方法之后,后面再加上returns,指定当前的返回数据类型,这是最为方便的一点。而另外呢,当前既然是类,它其实还可以有自己的属性,所以我们如果对于当前这一个关键字,我们如果直接写死在这里的话,显然是没有必要把它抽象出来,单独定义一个类的,那如果说我们想要去做抽象,那就可以把当前的关键字提取出来,当成一个专门的属性。那就是当前我们单独定义一个keyword。我们这个就不再叫做一个简单的filter了,可以把它叫做keyword filter,它本身还是一个filter function,但是里面呢,有一个自己的属性字段,叫做keyword。
06:02
我们在调用它的时候,可以直接给当前的构造方法传入一个参数,然后更改自己,当为当前的属性做一个赋值。那接下来我们要判断的呢,就是要看每一个数据到来的时候是否包含了对应的关键字,这样我们调用的过程当中就更加的灵活了。那当然就是匿名类的,这种方式呢,可能形式上会比单独的把类定义出来要更简单一点,但是就失去了这样的一种灵活操作。我们在实际应用当中,当然就可以根据需要去做选取了。啊,当然匿名类相对来讲简单一点,它还不足够简单,更加简单的当然是我们讲的第二种方式使用匿名函数拉姆达表达式了,它是正儿八引入的新特性,它的特点就是直接用一个向右的小箭头,然后把我们使用一个函数的定义,或者说一个方法的定义这种类似这种方式来代表一个。
07:08
单一抽象方法接口的实现类,而具体里边的这个定义的逻辑呢?很显然这里边的逻辑就跟对应的。我们函数类里边想要实现的那唯一的抽象方法是一样的啊,那这里边比如说这个map里边本身要传入一个map方式,而我们这里呢,可以直接传入一个拉姆达表达式,这里边跟map function里边必须实现的那个map方法的实现机制是完全一样。这里的event就是我们传入的参数每条数据,而这里的点URL就是返回值。啊,这就相当于是把当前的每一个事件转换成提取它的URL做一个转换输出,最终就得到的是一个string类型的数据了。这里需要注意的,那就是还是之前我们已经强调过的。
08:03
假如当前的al,也就是返回值的类型不是基本数据类型的话,如果是基本数据类型,我们这里不需要单独去指定啊,那flink可以自动解析,但假如说是一个复杂的类型,带着泛型的类型,或者是像flat map这样的函数,它的函数签名里面有collector,它本身就自带了泛型。那我们知道Z的编译器是会将泛型擦除掉,那接下来flink就没有办法去做类型的自动推断,那就会抛出异常。所以在使用拉姆达表达式的时候,一定要注意是否涉及到泛型类型,如果涉及到,那就不能直接使用。后边还要增加一段,那就是returns,指明当前的显示,指定当前的返回值内去。那最简单的返回方式呢,就是使用types,后面跟上types对应的一个具体的枚举类型,那还有一种方呢,当然就是可以直接使用type hint了,这两种方式都是可以的,在实际应用的过程当中呢,Type hint可能会更加的方便,更加简洁一些,当然直接用types也是完全没有问题的。
09:18
这就是关于匿名函数在编写代码过程当中的一些应用的注意事项。
我来说两句