2.flink泛型擦除《深入理解flink系列》

Java 8的Lambda表达式允许以一种直接的方式实现和传递函数,而不需要声明额外的(匿名的)类。Flink对Java API的所有操作符都支持使用lambda表达式,但是当lambda表达式使用了Java泛型时,开发者需要显式声明类型信息。如果开发者没有显式声明类型信息,那么使用lambda表达式会导致程序出错,这是Java编译器的泛型擦除问题所导致的。

1 泛型擦除

Java编译器在编译后会抛弃大部分的泛型类型信息,这被称为Java的泛型擦除。这意味着Flink应用程序在运行时,对象的一个实例不会知道它的泛型类型,例如DataStream<string>和DataStream<long>的实例在JVM中看起来是相同。

下面的示例map函数的输入和输出参数的类型不需要声明,因为它们是由Java编译器推断的。

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<Long> dataStream = env.generateSequence(1, 5);
// 在map操作上使用lambda表达式
DataStream<Long> resultStream=dataStream.map(i -> i*i);
resultStream.print();
env.execute();

Flink可以从方法签名OUT map(IN value)的实现中自动提取结果类型信息,但是对于具有泛型返回或输入类型的map函数Tuple2<Long, Long> map(Tuple2<Long, Long> value)会被Java编译器编译成Tuple2 map(Tuple2 value),这使得Flink不可能自动推断输入和输出类型的类型信息。

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<Long> dataStream = env.generateSequence(1, 5);
//将Long类型的数据流转换为一个Tuple2<Long, Long>类型的新数据流
DataStream<Tuple2<Long, Long>> mapStream=dataStream.map(new MapFunction<Long, Tuple2<Long, Long>>() {
    @Override
    public Tuple2<Long, Long> map(Long value) throws Exception {
        return new Tuple2<>(value, value);
    }
});
// 在map操作上使用lambda表达式
DataStream<Tuple2<Long, Long>> resultStream=mapStream.map(value -> value);
resultStream.print();
env.execute();

第二次map操作中数据流中的元素为Tuple2<Long, Long>具有泛型类型,而map操作使用了Lambda表达式导致Flink无法推断出泛型的类型,应用程序会抛出一个类似于下面的异常:

Caused by: org.apache.flink.api.common.functions.InvalidTypesException: The generic type parameters of 'Tuple2' 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.MapFunction' interface.
Otherwise the type has to be specified explicitly using type information.
    at org.apache.flink.api.java.typeutils.TypeExtractionUtils.validateLambdaType(TypeExtractionUtils.java:350)
    at org.apache.flink.api.java.typeutils.TypeExtractor.getUnaryOperatorReturnType(TypeExtractor.java:579)
    at org.apache.flink.api.java.typeutils.TypeExtractor.getMapReturnTypes(TypeExtractor.java:175)
    at org.apache.flink.streaming.api.datastream.DataStream.map(DataStream.java:587)
    ... 1 more

2 操作符上的return方法

对于Flink应用程序中操作符上使用Lambda表达式导致Flink无法推断出泛型的类型的问题,需要开发者在使用lambda表达式传递函数的操作符后面调用returns(…)方法来添加有关此操作符的返回类型的类型信息提示,否则输出将被视为Object类型,从而导致无效的序列化。

对上面的程序我们在mapStream数据流上调用map操作符之后调用returns(…)方法添加此操作符的返回类型的类型信息提示。

import org.apache.flink.api.common.typeinfo.Types;

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<Long> dataStream = env.generateSequence(1, 5);
//将Long类型的数据流转换为一个Tuple2<Long, Long>类型的数据流
DataStream<Tuple2<Long, Long>> mapStream=dataStream.map(new MapFunction<Long, Tuple2<Long, Long>>() {
    @Override
    public Tuple2<Long, Long> map(Long value) throws Exception {
        return new Tuple2<>(value, value);
    }
});
// 在map操作上使用lambda表达式
DataStream<Tuple2<Long, Long>> resultStream=mapStream.map(value -> value)
                                                //提供明确的类型信息
                                                .returns(Types.TUPLE(Types.LONG, Types.LONG))
resultStream.print();
env.execute();

returns(…)方法提供了三个重载方法:

  • public SingleOutputStreamOperator<T> returns(Class<T> typeClass):类可以用作非泛型类型(没有泛型参数的类)的类型提示,但不能用于诸如Tuples之类的泛型类型。对于那些泛型类型,请使用returns(TypeHint<T> typeHint)方法。

  • public SingleOutputStreamOperator<T> returns(TypeHint<T> typeHint):

    通过以下方式使用此方法:
    import org.apache.flink.api.common.typeinfo.TypeHint;
    DataStream<Tuple2<String, Double>> result =
              stream.flatMap(new FunctionWithNonInferrableReturnType())
                    .returns(new TypeHint<Tuple2<String, Double>>(){});
    
  • public SingleOutputStreamOperator<T> returns(TypeInformation<T> typeInfo):在大多数情况下,首选方法returns(Class)和returns(TypeHint)。

    通过以下方式使用此方法:
    import org.apache.flink.api.common.typeinfo.Types;
    DataStream<Tuple2<String, Double>> result =
              stream.flatMap(new FunctionWithNonInferrableReturnType())
                    .returns(Types.TUPLE(Types.String, Types.Double));
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暴躁IT老绵羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值