热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ApacheFlink中的Java泛型与Lambda表达式

  在使用Java编写apache-flink程序的时候相信很多新手都遇到下面这样的异常;org.apache.flink.api.common.functions.Invalid

  在使用Java编写apache-flink程序的时候相信很多新手都遇到下面这样的异常;

org.apache.flink.api.common.functions.InvalidTypesException: The return type of function 'main(DemoApp.java:29)' could not be determined automatically, due to type erasure. You can give type information hints by using the returns(...) method on the result of the transformation call, or by letting your function implement the 'ResultTypeQueryable' interface.
at org.apache.flink.api.dag.Transformation.getOutputType(Transformation.java:45

函数返回类型由于类型删除,无法自动确定类型;可使用returns方法或使用函数实现ResultTypeQueryable接口;

org.apache.flink.api.common.functions.InvalidTypesException: The generic type parameters of 'Collector' are missing. In many cases lambda methods don't provide enough information for automatic type extraction when Java generics are involved. An easy workaround is to use an (anonymous) class instead that implements the 'org.apache.flink.api.common.functions.FlatMapFunction' interface. Otherwise the type has to be specified explicitly using type information.

  Collector泛型类型参数丢失。在使用Java泛型时lambda方法无法提供足够的信息来进行自动进行类型提取。一个简单的解决方案是使用匿名内部类代替来实现FlatMapFunction接口,不然只能使用类型信息显式指定类型;

  抛出的上面这两个异常描述的内容其实很明确了,简单来说就是:在实现FlatMapFunction时使用lambda表达式导致了Collector变量的泛型类型参数丢(由于类型删除),简单的解决方案是把lambda表达式换成匿名内部类或者显式指定类型(使用returns方法或实现ResultTypeQueryable接口)
下面简单谈谈Java的类型擦除与flink的显式指定类型;  

Java类型擦除

  Java的泛型被很多人诟病称为“伪泛型”,也是因为类型擦除这个原因,泛型在Java中就是属于语法糖;
  在Java中JVM虚拟机层面并不存在泛型的概念,Java在编译阶段把泛型的类型参数给擦除掉了,在运行阶段并没有泛型的概念;

public class Data {
private T obj;

public T getObj() {
    return obj;
}

public void setObj(T obj) {
    this.obj = obj;
 }
}

  如上类,在经过Java编译成为class文件后其中的类型参数T将被擦除,字段obj变成了Object类型,两个get、set方法中的T也都换成了Object类型;

Apache-Flink中的Java泛型与Lambda表达式

泛型实现主要有两种:
  Code sharing:一个原始类的泛型类型只有一份目标代码。
  Code specialization:对每个泛型类型都生成不同的代码。
  Java属于第一种,C#与C++属于第二种,两种实现各有春秋吧,这里不讨论;
  为了保证Java的多态特性编译器在进行类型擦除时还可能会生成桥接方法用于保证类型擦除所导致子类与父类方法实现不一致问题;

Flink中的泛型与lambda

stream.flatMap(new FlatMapFunction() {
        @Override
        public void flatMap(Integer value, Collector out) throws Exception {
            System.out.println(value);
        }
    });

  在Flink中使用各种算子的时候可能会有类似上面面这种用法,上面这种方式使用并没有什么问题,这里的FlatMapFunction就是一个泛型接口,使用了匿名内部类实现了该接口并传递给了flatMap算子;

stream.flatMap((FlatMapFunction) (value, out) -> {
        System.out.println(value);
})

  也有的人直接使用lambda表达式实现FlatMapFunction接口传递给flatMap算子,但这时候很多新手估计会发现程序运行的时候报错了,抛出了本文最开始的那两个异常;
  为什么使用匿名内部类就没问题,而使用lambda表达式就不行报错了,其实异常信息已经描述很清楚了。这里简单看看为什么匿名内部类可以,lambda表达式不可以,使用returns方法或实现ResultTypeQueryable接口也可以;

  上面介绍了在Java中会对泛型信息进行类型参数擦除,但在这里为啥使用匿名内部类实现FlatMapFunction时却还是可以获取得到泛型参数?
  其实Java中编译时的泛型类型擦除并不是把所以泛型相关的信息全部擦干干净净,Javac编译时擦除的只是结构化之外(程序执行流)的信息这部分信息存储在字节码的Code属性中,类、字段、方法的泛型类型参数元数据都会被保留下来,这些存储在Signature属性中;可通过反射得到相关的泛型参数信息;

s.flatMap(new FlatMapFunction() {
        @Override
        public void flatMap(String value, List out) {
            System.out.println("stu");
        }
    });

Apache-Flink中的Java泛型与Lambda表达式

  而lambda表达式实现FlatMapFunction却获取不到泛型参数,是的。
  匿名内部类会编译成相关的类字节码存储在class文件中,而lambda表达式却也只是Java的语法糖并不会存在相关的类字节码,只会在lambda表达式运行时调用invokedynamic指令执行逻辑。lambda表达式丢失了更多的类型信息,也就导致了使用lambda表达式获取不到泛型类型参数;

s.flatMap((FlatMapFunction) (value, out) -> 
System.out.println("stu"));

Apache-Flink中的Java泛型与Lambda表达式

Flink中使用lambda后的写法
  其实上面异常信息已经说得非常清楚了,调用returns方法或实现ResultTypeQueryable接口,这里就简单说这两种用法;

returns方法
  调用该方法的用法也比较简单,就是返回的Collector需要哪个泛型类型参数你就调用returns方法注册哪种类型,调用returns方法一定是要在某个算子之后紧接着第一个调用,简单理解就是未某个算子注册返回类型;

 stream.flatMap((FlatMapFunction) (value, out) -> {
        System.out.println(value);
    })
    .returns(String.class)  

ResultTypeQueryable接口
  实现此接口就可以告诉系统此算子的返回值类型,实现了此接口的优先级最高,不会再通过反射去获取返回值类型。还可以根据类型参数的不同使用不同的返回值类型;实现此接口可定制化程度很高、灵活。Flink kafka相关的连接器中就是用了这种模式。

public class FlatFun implements ResultTypeQueryable, FlatMapFunction {
@Override
public TypeInformation getProducedType() {
    return TypeInformation.of(String.class);
}
@Override
public void flatMap(Integer value, Collector out) {
    out.collect(String.valueOf(value));
    System.out.println("flatFun");
}
}
stream.flatMap(new FlatFun())
            .print();

推荐阅读
  • 本文将介绍如何在混合开发(Hybrid)应用中实现Native与HTML5的交互,包括基本概念、学习目标以及具体的实现步骤。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 使用HTML和JavaScript实现视频截图功能
    本文介绍了如何利用HTML和JavaScript实现从远程MP4、本地摄像头及本地上传的MP4文件中截取视频帧,并展示了具体的实现步骤和示例代码。 ... [详细]
  • C语言编写线程池的简单实现方法
    2019独角兽企业重金招聘Python工程师标准好文章,一起分享——有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带 ... [详细]
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • 本文详细介绍了 com.apollographql.apollo.api.internal.Optional 类中的 orNull() 方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。 ... [详细]
  • JVM钩子函数的应用场景详解
    本文详细介绍了JVM钩子函数的多种应用场景,包括正常关闭、异常关闭和强制关闭。通过具体示例和代码演示,帮助读者更好地理解和应用这一机制。适合对Java编程和JVM有一定基础的开发者阅读。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • 本文介绍如何使用OpenCV和线性支持向量机(SVM)模型来开发一个简单的人脸识别系统,特别关注在只有一个用户数据集时的处理方法。 ... [详细]
  • Flutter 2.* 路由管理详解
    本文详细介绍了 Flutter 2.* 中的路由管理机制,包括路由的基本概念、MaterialPageRoute 的使用、Navigator 的操作方法、路由传值、命名路由及其注册、路由钩子等。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
author-avatar
Yomon-00
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有