【问题标题】:Java Generics: chaining together generic function objectJava 泛型:将泛型函数对象链接在一起
【发布时间】:2011-12-30 14:46:33
【问题描述】:

我一直在努力解决以下问题。我有一系列函数对象,每个函数对象都有自己的输入和输出类型,这些类型是通过 java 中的泛型类型参数定义的。我想将它们排列成一个链,以便原始数据输入到第一个函数,转换为输出类型,即下一个对象的输入类型,依此类推。当然,这对于硬编码来说是微不足道的,但我希望代码可以插入到新的函数对象中。如果我只是省略了类型参数(只有最终的输出类型),这就是事情的样子:

    public T process() {
        Iterator<Context> it = source.provideData();
        for(Pipe pipe : pipeline) {
            it = pipe.processIterator(it);
        }
        return sink.next(it);
    }

这里在函数对象之间传递数据的迭代器,上下文应该是上下文。有没有办法保持以下类型的管道可插拔并仍然保持类型安全?

编辑: 为清楚起见,我有一系列功能对象,管道。每个都将特定类型作为输入并输出另一种类型。 (实际上是这些类型的迭代器)这些将链接在一起,例如Pipe&lt;A,B&gt; -&gt; Pipe&lt;B,C&gt; -&gt; Pipe&lt;C,D&gt; -&gt; ...,因此一个管道的输出是下一个管道的输入类型。这里还有一个输出 A 类型迭代器的源和一个接受类型的接收器(过去管道的输出)。这会让事情变得更清楚吗?问题是,由于对输入和输出类型的兼容性存在严重依赖关系,有没有办法确保这一点?

我开始认为将函数对象插入管道可能是确保类型安全的最佳时机,但我不确定如何执行此操作。

编辑 2: 我有一个函数对象的加法器方法,目前如下所示:

public void addPipe(Pipe<?,?> pipe) {
    pipeline.add(pipe);
}

我想检查第一个类型参数是否与当前管道的“结束”相同,如果不是则抛出异常?我不认为这里有一个很好的方法来获得编译时的安全性。然后可以将当前管道的“结束”设置为输入管道的第二种类型参数。我想不出如何使用泛型来做到这一点,并且传递类信息似乎非常可怕。

【问题讨论】:

  • 类型擦除可能会让您对完全通用的功能更加困难。 JVM 字节编译器从泛型类的实例中删除类型,因此 List 变为 List
  • 您能再解释一下需求吗?无法弄清楚自己拥有什么以及想要拥有什么。
  • 那么最好的方法是在插入管道函数对象时尝试确保类型安全(如何??),并在上述方法中抑制警告?
  • 试图澄清。希望对您有所帮助。

标签: java generics chain-of-responsibility


【解决方案1】:

这是另一种方法:这种方法允许转换步骤产生一个列表。例如,转换可以将一个字符串拆分为多个子字符串。此外,如果转换任何值产生异常,它允许通用异常处理代码。它还允许使用空列表作为返回值,而不是必须测试的模棱两可的空值以避免 NullPointerException。这个问题的主要问题是它在移动到下一步之前会完整地执行每个转换步骤,这可能不是内存效率。

public class Chain<IN, MEDIAL, OUT> {
    private final Chain<IN, ?, MEDIAL> head;
    private final Transformer<MEDIAL, OUT> tail;

    public static <I, O> Chain<I, I, O> makeHead(@Nonnull Transformer<I, O> tail) {
        return new Chain<>(null, tail);
    }

    public static <I, M, O> Chain<I, M, O> append(@Nonnull Chain<I, ?, M> head, @Nonnull Transformer<M, O> tail) {
        return new Chain<>(head, tail);
    }

    private Chain(@Nullable Chain<IN, ?, MEDIAL> head, @Nonnull Transformer<MEDIAL, OUT> tail) {
        this.head = head;
        this.tail = tail;
    }

    public List<OUT> run(List<IN> input) {
        List<OUT> allResults = new ArrayList<>();

        List<MEDIAL> headResult;
        if (head == null) {
            headResult = (List<MEDIAL>) input;
        } else {
            headResult = head.run(input);
        }

        for (MEDIAL in : headResult) {
            // try/catch here
            allResults.addAll(tail.transform(in));
        }

        return allResults;
    }

    public static void main(String[] args) {

        Transformer<String, Integer> pipe1 = new Transformer<String, Integer>() {
            @Override
            public List<Integer> transform(String s) {
                return Collections.singletonList(Integer.valueOf(s) * 3);
            }
        };
        Transformer<Integer, Long> pipe2 = new Transformer<Integer, Long>() {
            @Override
            public List<Long> transform(Integer s) {
                return Collections.singletonList(s.longValue() * 5);
            }
        };
        Transformer<Long, BigInteger> pipe3 = new Transformer<Long, BigInteger>() {
            @Override
            public List<BigInteger> transform(Long s) {
                return Collections.singletonList(new BigInteger(String.valueOf(s * 7)));
            }
        };
        Chain<String, ?, Integer> chain1 = Chain.makeHead(pipe1);
        Chain<String, Integer, Long> chain2 = Chain.append(chain1, pipe2);
        Chain<String, Long, BigInteger> chain3 = Chain.append(chain2, pipe3);
        List<BigInteger> result = chain3.run(Collections.singletonList("1"));
        System.out.println(result);
    }
}

【讨论】:

  • 显然,如果您能够使用 Java 8,那么您会想要使用 Streams。或者,类似 RxJava。
【解决方案2】:

这是一种方法。 run 方法不是类型安全的,但鉴于附加管道的唯一方法是以类型安全的方式进行,因此整个链是类型安全的。

public class Chain<S, T> {
    private List<Pipe<?, ?>> pipes;

    private Chain() {
    }

    public static <K, L> Chain<K, L> start(Pipe<K, L> pipe) {
        Chain<K, L> chain = new Chain<K, L>();
        chain.pipes = Collections.<Pipe<?, ?>>singletonList(pipe);;
        return chain;
    }

    public <V> Chain<S, V> append(Pipe<T, V> pipe) {
        Chain<S, V> chain = new Chain<S, V>();
        chain.pipes = new ArrayList<Pipe<?, ?>>(pipes);
        chain.pipes.add(pipe);
        return chain;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public T run(S s) {
        Object source = s;
        Object target = null;
        for (Pipe p : pipes) {
            target = p.transform(source);
            source = target;
        }
        return (T) target;
    }

    public static void main(String[] args) {
        Pipe<String, Integer> pipe1 = new Pipe<String, Integer>() {
            @Override
            public Integer transform(String s) {
                return Integer.valueOf(s);
            }
        };
        Pipe<Integer, Long> pipe2 = new Pipe<Integer, Long>() {
            @Override
            public Long transform(Integer s) {
                return s.longValue();
            }
        };
        Pipe<Long, BigInteger> pipe3 = new Pipe<Long, BigInteger>() {
            @Override
            public BigInteger transform(Long s) {
                return new BigInteger(s.toString());
            }
        };
        Chain<String, BigInteger> chain = Chain.start(pipe1).append(pipe2).append(pipe3);
        BigInteger result = chain.run("12");
        System.out.println(result);
    }
}

【讨论】:

  • 真的很酷,并且像所有好的解决方案一样,一旦知道就很明显:)
  • 感谢您的回答 :) 我根据您的回答创建了一个 github project,添加了异步功能和并行执行。希望它会对某人有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
  • 1970-01-01
  • 2013-01-08
  • 2012-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多