Java 你能实现Flink';具有泛型类型的AggregateFunction?

Java 你能实现Flink';具有泛型类型的AggregateFunction?,java,generics,interface,apache-flink,Java,Generics,Interface,Apache Flink,我的目标是为Flink 1.10中的流处理模块提供一个接口。管道在其他操作符中包含AggregateFunction。所有运算符都有泛型类型,但问题在于AggregateFunction,它无法确定输出类型 注意:实际的管道有一个slidingEventTimeWindow赋值器和一个随AggregateFunction一起传递的WindowFunction,但是使用下面的代码可以更容易地再现错误 这是一个复制错误的简单测试用例: @Test public void aggreg

我的目标是为Flink 1.10中的流处理模块提供一个接口。管道在其他操作符中包含AggregateFunction。所有运算符都有泛型类型,但问题在于AggregateFunction,它无法确定输出类型

注意:实际的管道有一个slidingEventTimeWindow赋值器和一个随AggregateFunction一起传递的WindowFunction,但是使用下面的代码可以更容易地再现错误

这是一个复制错误的简单测试用例:

    @Test
    public void aggregateFunction_genericType() throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        DataStream<Tuple2<String,Integer>> source = env.fromElements(Tuple2.of("0",1), Tuple2.of("0",2), Tuple2.of("0",3));

        ConfigAPI cfg = new ConfigAPI();

        source
                .keyBy(k -> k.f0)
                .countWindow(5, 1)
                .aggregate(new GenericAggregateFunc<>(cfg))
                .print();


        env.execute();
    }
解决方案1(不工作): 起初我认为这是“无法确定返回类型”的常见情况,所以我尝试添加

.returns(Types.TUPLE(Types.STRING,Types.INT))
.aggregate(…)
之后,但未成功返回

解决方案2(工作): 我创建了一个具有泛型类型的包装器类,名为
acculator
,然后作为类型传递给
AggregateFunction
似乎正在运行

这看起来不是很优雅,但是它与界面的其他部分不太一致。这个问题还有别的解决办法吗

编辑:感谢@dedupler为您提供的时间和洞察力,我想我找到了解决方案

解决方案3(工作):我创建了一个新接口,它以以下方式扩展了我的
BaseConfigAPI
AggregateFunction

public interface MergedConfigAPI<In, Acc, Out> extends BaseConfigAPI, AggregateFunction<In, Acc, Out> {}

public interface BaseConfigAPI extends Serializable {
    //These will be implemented directly from AggregateFunction interface
    //Acc createAcc();
    //Acc addAccumulators(In in, Acc acc);
        
    //other methods to override
}
注:从Flink 1.10.1开始,
aggregate
方法用@publicEvolution注释

“您能否使用泛型类型实现Flink的聚合功能?”

是的。你可以。就像你自己已经做的那样。你的错误是由于你如何使用它(如“使用站点泛型”)而不是你如何实现它

“是否有其他解决此问题的方法?”

我提出以下三个候选解决方案,按简单性的升序排列

虽然这一个涉及到一个小的重构,但在我看来,它比第一个提出的解决方案更简化了整个应用程序

Flink已经为您处理了“复杂”的泛型多态性。要插入Flink,您所要做的只是使用您想要实例化的特定类型参数实例化其内置的泛型
AggregateFunction
。在您的情况下,这些类型参数的类型为
Tuple2

因此,您仍然在第二种解决方案中“使用泛型”,但您使用的方式要简单得多

另一个选项更接近您的原始实现,但有几个小的重构

public static class GenericAggregateFunc<In, Acc, Out> implements AggregateFunction<In, Acc, Out> {
    
    ...
    @Override
    public Out getResult(Acc acc) {
        return ...;
    }
    ...
}
public静态类GenericAggregateFunc实现AggregateFunction{
...
@凌驾
公开输出结果(Acc){
返回。。。;
}
...
}
此外,要强制用户的配置实现与您的函数兼容的接口,必须满足以下前提条件

public interface BaseConfigAPI< In, Acc, Out >{ ... }
公共接口BaseConfigAPI{…} 在中,我已经确认将
Out
类型参数也添加到
BaseConfigAPI
中,使其兼容


我确实想到了一个更复杂的替代解决方案。但由于简单几乎总是更好,我将把更复杂的解决方案留给其他人提出。

为了检查我是否正确理解:堆栈跟踪中提到的
滑动窗口
:misc.SlidingWindow.aggregateFunction\u genericType中的
(SlidingWindow.java:54)
“您的问题中的第一组代码是什么?
@Test aggregateFunction\u genericType()
方法是“复制错误的简单测试用例”“。错误消息显示:“…
Type
TypeVariable
Acc
“类
杂项滑动窗口$GenericAggregateFunc
”中的
Type
”无法确定。。。“。您假设的
Acc
是什么类型?哪一行代码支持您假设的
Acc
应该是那种类型?@deduder是的,第一个代码块与您正确理解的堆栈跟踪中的代码块相对应。我假设
Acc
Tuple2
类型,但我无法支持这种假设。”因此,在将其作为参数传递给
.aggregate()
操作符之前,我尝试初始化
genericaggeratefunc aggregateFunc
函数,如下所示:
genericaggeratefunc aggregateFunc=new genericaggeratefunc(cfg)
。此添加是否支持我的假设?“感谢@dedupper为我提供的时间和见解,我想我找到了一个解决方案。。。“-欢迎您,先生!&感谢您分享这么好的问题。我学到了很多解决方法。为了我的进一步教育-因为我没有在本地运行/测试-我最近的解决方案候选者在您的
aggregateFunction\u genericType()中的表现如何
test?我指的是我添加了第三个类型参数的那一个:
GenericAggregateFunc
。另外,我的假设是否正确,即您的新解决方案是从我的Q上次编辑中获得线索的?只是好奇。@dedumer谢谢:)我最终没有在测试中使用您的解决方案,因为我决定完全放弃
GenericAggregateFunc
,让用户实现它。然而,您的所有评论和编辑都只是通过在每一步都质疑问题的正确性而为解决方案做出了贡献。我最终没有在测试中使用您的解决方案,因为e我决定完全放弃GenericAggregateFunc…-是的,这就是我理解你的意思。但我有点想请你帮个忙:因为我没有在本地安装必要的框架(这样会很不方便)既然你已经安装了必要的框架,你能不能运行一下,我建议的实现,看看她是怎么做的?主要是智力上的好奇心。比我的好奇心更重要的是,你的回答会缩小或缩小
org.apache.flink.api.common.functions.InvalidTypesException: 
Type of TypeVariable 'Acc' in 'class misc.SlidingWindow$GenericAggregateFunc' could not be determined. This is most likely a type erasure problem. 
The type extraction currently supports types with generic variables only in cases where all variables in the return type can be deduced from the input type(s). 
Otherwise the type has to be specified explicitly using type information.
public interface MergedConfigAPI<In, Acc, Out> extends BaseConfigAPI, AggregateFunction<In, Acc, Out> {}

public interface BaseConfigAPI extends Serializable {
    //These will be implemented directly from AggregateFunction interface
    //Acc createAcc();
    //Acc addAccumulators(In in, Acc acc);
        
    //other methods to override
}
 @Test
 public void aggregateFunction_genericType() throws Exception {
                ...

                .aggregate(
                        new GenericAggregateFunc<>(cfg), 
                        Types.TUPLE(Types.STRING, Types.INT),
                        Types.TUPLE(Types.STRING, Types.INT))
                ...
    }
...
source
       .keyBy(k -> k.f0)
       .countWindow(5, 1)
       .aggregate(new GenericAggregateFunc< Tuple2<String, Integer>, Tuple2<String, Integer> >(cfg)) /* filling in the diamond will aid type inference */
       .print();
...
public static class GenericAggregateFunc implements AggregateFunction<Tuple2<String, Integer>, Tuple2<String, Integer>, Tuple2<String, Integer>> {

    private BaseConfigAPI<Tuple2<String, Integer>, Tuple2<String, Integer>> cfg;
    GenericAggregateFunc(BaseConfigAPI<Tuple2<String, Integer>, Tuple2<String, Integer>> cfg) {
        this.cfg = cfg;
    }
    @Override
    public Tuple2<String, Integer> createAccumulator() {
        return cfg.createAcc();
    }
    @Override
    public Tuple2<String, Integer> add(Tuple2<String, Integer> in, Tuple2<String, Integer> acc) {
        return cfg.addAccumulators(in, acc);
    }
    @Override
    public Tuple2<String, Integer> getResult(Tuple2<String, Integer> acc) {
        return acc;
    }
    @Override
    public Tuple2<String, Integer> merge(Tuple2<String, Integer> acc, Tuple2<String, Integer> acc1) {
        return null;
    }
}
public static class GenericAggregateFunc<In, Acc, Out> implements AggregateFunction<In, Acc, Out> {
    
    ...
    @Override
    public Out getResult(Acc acc) {
        return ...;
    }
    ...
}
public interface BaseConfigAPI< In, Acc, Out >{ ... }