gpt4 book ai didi

java - 如何使用 Java 泛型编写管道类

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:02:10 25 4
gpt4 key购买 nike

以下类声明了一个管道来处理数据项。这目前工作正常。但是 Pipeline 类中有警告,我不知道如何正确修复:

public abstract class Stage<In, Out> {
public abstract DataItem<Out> processItem(DataItem<In> data) throws Exception;
}

public class DataItem<T> {
public T data;

public DataItem(T obj) {
this.data = obj;
}
}

// chain Stages together
public class Pipeline {
private List<Stage<?,?>> stages;

public Pipeline(List<Stage<?,?>> stages) {
this.stages = stages;
}

// WARNING HERE: DataItem is a raw type. References to generic type should be parameterized
public void processItem(DataItem dataItem) throws Exception {

for (Stage<?, ?> stage: stages) {
// WARNING HERE: DataItem is a raw type. References to generic type should be parameterized
dataItem = stage.processItem(dataItem);
}
}

我尝试通过添加通配符来更改 processItem() 声明,但这会导致编译错误:

  public void processItem(DataItem<?> dataItem) throws Exception {
for (Stage<?, ?> stage: stages) {
// COMPILATION ERROR: The method processItem(DataItem<capture#2-of ?>)
// in the type Stage<capture#2-of ?,capture#3-of ?> is not applicable
// for the arguments (DataItem<capture#4-of ?>)
dataItem = stage.processItem(dataItem);
}
}

有解决办法吗?

最佳答案

首先,警告仅源于 Pipeline 的事实本身不是通用的。它不携带任何关于它期望的初始输入的信息。

我对解决此问题的建议涉及一些其他概括和(我认为是)改进:

  • Stage成为一个界面。当您有一个包含 抽象方法的抽象类时,这通常应该是一个接口(interface)。无需将实现者固定到特定的 extends Stage当他们可以等效地做extends TheirBase implements Stage .

  • 不要低估第一点。它允许你说 Pipeline implements Stage .想想这为用户带来的力量。他们可以在 Pipeline 中组装复杂的流程, 然后简单地使用这个 Pipeline作为一个Stage在一个更复杂的过程中。从本质上讲,这是 Flow-Based Programming 的核心概念之一。 !

  • 创建一个类型安全 Builder对于 Pipeline对象。

最后一点是您最初的问题。这个构建器类中最重要的方法就是这个:

public <NextOut> PipelineBuilder<In, NextOut> add(Stage<Out, NextOut> stage)
{
stages.add(stage);

// This cast is safe as per construction of the pipeline
@SuppressWarnings("unchecked")
PipelineBuilder<In, NextOut> result =
(PipelineBuilder<In, NextOut>) this;
return result;
}

这个想法是,只要你有管道的构建器 <S, T> ,并附加一个类型为 <T, U> 的阶段, 然后构建器将成为类型为 <S, U> 的构建器.

(如您所见,它包含一个未经检查的转换,但根据构建器和管道的构建和后续使用,转换是安全的)

这些转换可以写成过于复杂的形式:

private static void showTypeTransitions()
{
Stage<String, String[]> s0 = null;
Stage<String[], List<Integer>> s1 = null;
Stage<List<Integer>, Integer> s2 = null;

// Starting with a builder for a Pipeline<String, String[]>
PipelineBuilder<String, String[]> b0 = PipelineBuilder.create(s0);

// Appending a Stage<String[], List<Integer>> turns it
// into a builder for a Pipeline<String, List<Integer>>
PipelineBuilder<String, List<Integer>> b1 = b0.add(s1);

// Appending a Stage<List<Integer>, Integer> turns it
// into a builder for a Pipeline<String, Integer>
PipelineBuilder<String, Integer> b2 = b1.add(s2);

// Finally, build it
Pipeline<String, Integer> pipeline = b2.build();
}

但这不是必须的。构建器的目的是为构建提供一个流畅的界面:

Pipeline<String, Integer> pipeline = PipelineBuilder
.create(splitter)
.add(transformer)
.add(accumulator)
.build();

(请注意,您也可以省略 Builder,并向 Pipeline 类添加类似的方法。但这需要对构造函数等进行一些调整)。


这是一个展示这种方法的 MCVE。在showBasicUsage方法,显示了基本用法(因此得名...)。在showHowNiceItIsToUseAnInterface方法,在基本示例中创建的管道用作一个 Stage一个新的 Pipeline即已创建。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

interface Stage<In, Out>
{
DataItem<Out> processItem(DataItem<In> data) throws Exception;
}

class DataItem<T>
{
public T data;

public DataItem(T obj)
{
this.data = obj;
}
}

class Pipeline<In, Out> implements Stage<In, Out>
{
private List<Stage<?, ?>> stages;

Pipeline(List<Stage<?, ?>> stages)
{
this.stages = stages;
}

@Override
public DataItem<Out> processItem(DataItem<In> dataItem) throws Exception
{
DataItem<?> current = dataItem;
for (Stage<?, ?> stage : stages)
{
current = apply(stage, current);
}

// This cast is safe as per construction of the pipeline
@SuppressWarnings("unchecked")
DataItem<Out> result = (DataItem<Out>) current;
return result;
}

private <I, O> DataItem<O> apply(
Stage<I, O> stage, DataItem<?> dataItem) throws Exception
{
// This cast is safe as per construction of the pipeline
@SuppressWarnings("unchecked")
DataItem<I> typedDataItem = (DataItem<I>)dataItem;
DataItem<O> result = stage.processItem(typedDataItem);
return result;
}
}

class PipelineBuilder<In, Out>
{
private List<Stage<?, ?>> stages;

static <In, Out> PipelineBuilder<In, Out> create(Stage<In, Out> stage)
{
PipelineBuilder<In, Out> pipelineBuilder =
new PipelineBuilder<In, Out>(stage);
return pipelineBuilder;
}
private PipelineBuilder(Stage<In, Out> stage)
{
stages = new ArrayList<Stage<?,?>>();
stages.add(stage);
}
public <NextOut> PipelineBuilder<In, NextOut> add(Stage<Out, NextOut> stage)
{
stages.add(stage);

// This cast is safe as per construction of the pipeline
@SuppressWarnings("unchecked")
PipelineBuilder<In, NextOut> result =
(PipelineBuilder<In, NextOut>) this;
return result;
}

public Pipeline<In, Out> build()
{
return new Pipeline<In, Out>(stages);
}
}

public class PipelineExample
{
public static void main(String[] args) throws Exception
{
showBasicUsage();
showHowNiceItIsToUseAnInterface();
}

private static void showBasicUsage() throws Exception
{
Pipeline<String, Integer> pipeline = createExamplePipeline();

DataItem<String> in = new DataItem<>("1 35 42 2 10 5 2 3");
DataItem<Integer> out = pipeline.processItem(in);

System.out.println(out.data); // prints 100
}


private static void showHowNiceItIsToUseAnInterface() throws Exception
{
Stage<List<Integer>, String> stringCreator =
dataItem -> new DataItem<>(
dataItem.data.stream()
.map(String::valueOf)
.collect(Collectors.joining(" ")));

// Create the whole pipeline that was used in the basic usage
// example, and use it as one stage in the new pipeline:
Stage<String, Integer> pipelineAsStage = createExamplePipeline();

Pipeline<List<Integer>, Integer> pipeline = PipelineBuilder
.create(stringCreator)
.add(pipelineAsStage)
.build();

DataItem<List<Integer>> in = new DataItem<>(Arrays.asList(0,1,2,3,4));
DataItem<Integer> out = pipeline.processItem(in);

System.out.println(out.data); // prints 10
}

private static Pipeline<String, Integer> createExamplePipeline()
{
Stage<String, String[]> splitter =
dataItem -> new DataItem<>(dataItem.data.split(" "));
Stage<String[], List<Integer>> transformer =
dataItem -> new DataItem<>(Arrays.stream(dataItem.data)
.map(Integer::parseInt)
.collect(Collectors.toList()));
Stage<List<Integer>, Integer> accumulator =
dataItem -> new DataItem<>(
dataItem.data.stream().reduce(0, Integer::sum));

Pipeline<String, Integer> pipeline = PipelineBuilder
.create(splitter)
.add(transformer)
.add(accumulator)
.build();

return pipeline;
}



private static void showTypeTransitions()
{
Stage<String, String[]> s0 = null;
Stage<String[], List<Integer>> s1 = null;
Stage<List<Integer>, Integer> s2 = null;

// Starting with a builder for a Pipeline<String, String[]>
PipelineBuilder<String, String[]> b0 = PipelineBuilder.create(s0);

// Appending a Stage<String[], List<Integer>> turns it
// into a builder for a Pipeline<String, List<Integer>>
PipelineBuilder<String, List<Integer>> b1 = b0.add(s1);

// Appending a Stage<List<Integer>, Integer> turns it
// into a builder for a Pipeline<String, Integer>
PipelineBuilder<String, Integer> b2 = b1.add(s2);

// Finally, build it
Pipeline<String, Integer> pipeline = b2.build();
}

}

(这些阶段取自 the answer from Kirill Simonov ,只是为了方便起见,因为它们是一个很好的示例用例)

关于java - 如何使用 Java 泛型编写管道类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48408453/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com