- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
以下类声明了一个管道来处理数据项。这目前工作正常。但是 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/
我正在使用 Assets 管道来管理我的 Grails 3.0 应用程序的前端资源。但是,似乎没有创建 CoffeeScript 文件的源映射。有什么办法可以启用它吗? 我的 build.gradle
我有一个我想要的管道: 提供一些资源, 运行一些测试, 拆资源。 我希望第 3 步中的拆卸任务运行 不管 测试是否通过或失败,在第 2 步。据我所知 runAfter如果前一个任务成功,则只运行一个任
如果我运行以下命令: Measure-Command -Expression {gci -Path C:\ -Recurse -ea SilentlyContinue | where Extensio
我知道管道是一个特殊字符,我需要使用: Scanner input = new Scanner(System.in); String line = input.next
我再次遇到同样的问题,我有我的默认处理方式,但它一直困扰着我。 有没有更好的办法? 所以基本上我有一个运行的管道,在管道内做一些事情,并想从管道内返回一个键/值对。 我希望整个管道返回一个类型为 ps
我有三个环境:dev、hml 和 qa。 在我的管道中,根据分支,阶段有一个条件来检查它是否会运行: - stage: Project_Deploy_DEV condition: eq(varia
我有 Jenkins Jenkins ver. 2.82 正在运行并想在创建新作业时使用 Pipeline 功能。但我没有看到这个列为选项。我只能在自由式项目、maven 项目、外部项目和多配置之间进
在对上一个问题 (haskell-data-hashset-from-unordered-container-performance-for-large-sets) 进行一些观察时,我偶然发现了一个奇
我正在寻找有关如何使用管道将标准输出作为其他命令的参数传递的见解。 例如,考虑这种情况: ls | grep Hello grep 的结构遵循以下模式:grep SearchTerm PathOfFi
有没有办法不因声明性管道步骤而失败,而是显示警告?目前我正在通过添加 || exit 0 来规避它到 sh 命令行的末尾,所以它总是可以正常退出。 当前示例: sh 'vendor/bin/phpcs
我们正在从旧的 Jenkins 设置迁移到所有计划都是声明性 jenkinsfile 管道的新服务器……但是,通过使用管道,我们无法再手动清除工作区。我如何设置 Jenkins 以允许 手动点播清理工
我在 Python 中阅读了有关 Pipelines 和 GridSearchCV 的以下示例: http://www.davidsbatista.net/blog/2017/04/01/docume
我有一个这样的管道脚本: node('linux'){ stage('Setup'){ echo "Build Stage" } stage('Build'){ echo
我正在使用 bitbucket 管道进行培训 这是我的 bitbucket-pipelines.yml: image: php:7.2.9 pipelines: default:
我正在编写一个程序,其中输入文件被拆分为多个文件(Shamir 的 secret 共享方案)。 这是我想象的管道: 来源:使用 Conduit.Binary.sourceFile 从输入中读取 导管:
我创建了一个管道,它有一个应该只在开发分支上执行的阶段。该阶段还需要用户输入。即使我在不同的分支上,为什么它会卡在这些步骤的用户输入上?当我提供输入时,它们会被正确跳过。 stage('Deplo
我正在尝试学习管道功能(%>%)。 当试图从这行代码转换到另一行时,它不起作用。 ---- R代码--原版----- set.seed(1014) replicate(6,sample(1:8))
在 Jenkins Pipeline 中,如何将工件从以前的构建复制到当前构建? 即使之前的构建失败,我也想这样做。 最佳答案 Stuart Rowe 还在 Pipeline Authoring Si
我正在尝试使用 执行已定义的作业构建 使用 Jenkins 管道的方法。 这是一个简单的例子: build('jenkins-test-project-build', param1 : 'some-
当我使用 where 过滤器通过管道命令排除对象时,它没有给我正确的输出。 PS C:\Users\Administrator> $proall = Get-ADComputer -filter *
我是一名优秀的程序员,十分优秀!