Java泛型:将泛型函数对象链接在一起
我一直在努力解决以下问题。我有一系列函数对象,每个对象都有自己的输入和输出类型,这些类型是通过java中的泛型类型参数定义的。我想把它们排列成一个链,以便将原始数据输入到第一个函数,转换为输出类型,这是下一个对象的输入类型,依此类推。当然,这对于硬代码来说是微不足道的,但是我希望代码能够插入到新的函数对象中。如果我只忽略了类型参数(只有最终的输出类型),情况就是这样:Java泛型:将泛型函数对象链接在一起,java,generics,chain-of-responsibility,Java,Generics,Chain Of Responsibility,我一直在努力解决以下问题。我有一系列函数对象,每个对象都有自己的输入和输出类型,这些类型是通过java中的泛型类型参数定义的。我想把它们排列成一个链,以便将原始数据输入到第一个函数,转换为输出类型,这是下一个对象的输入类型,依此类推。当然,这对于硬代码来说是微不足道的,但是我希望代码能够插入到新的函数对象中。如果我只忽略了类型参数(只有最终的输出类型),情况就是这样: public T process() { Iterator<Context> it = so
public T process() {
Iterator<Context> it = source.provideData();
for(Pipe pipe : pipeline) {
it = pipe.processIterator(it);
}
return sink.next(it);
}
public T进程(){
迭代器it=source.provideData();
用于(管道:管道){
it=pipe.process迭代器(it);
}
返回水槽。下一步(it);
}
在这里,数据的迭代器在函数对象之间传递,上下文应该是上下文。有没有办法保持以下类型的管道可插拔,同时保持类型安全
编辑:
为了清楚起见,我有一系列的函数对象,管道。每一个都将一个特定类型作为输入,并输出另一个类型。(实际上是这些类型的迭代器)这些类型将链接在一起,例如,Pipe->Pipe->Pipe->Pipe->……
,这样一个管道的输出就是下一个管道的输入类型。这里还有一个输出类型为a的迭代器的源和一个接受类型(过去管道的输出)的接收器。这能让事情更清楚吗?问题是,因为存在对输入和输出类型兼容性的关键依赖,有没有办法确保这一点
我开始认为,将函数对象插入到管道中可能是确保类型安全的最佳时机,但我不确定如何做到这一点
编辑2:
我有一个用于函数对象的加法器方法,目前如下所示:
public void addPipe(Pipe<?,?> pipe) {
pipeline.add(pipe);
}
public void addPipe(管道){
管道。添加(管道);
}
我想检查第一个类型参数是否与当前管道的“end”相同,如果不相同,则抛出异常?我不认为这里有一个好的方法来保证编译时的安全性。然后,可以将当前管道的“端点”设置为输入管道的第二个类型参数。我想不出如何使用泛型来实现这一点,而传递类信息似乎非常可怕 这里有一个方法。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);
}
}
公共类链{
私有列表>单音列表(管道);;
返回链;
}
公共链附加(管道){
链=新链();
chain.pipes=new ArrayList还有另一种方法:这种方法允许转换步骤生成一个列表。例如,转换可以将一个字符串拆分为多个子字符串。此外,如果转换任何值产生异常,它还允许使用公共异常处理代码。它还允许使用空List作为一个返回值,而不是一个必须进行测试以避免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);
}
}
公共类链{
专用最终链头;
专用最终变压器尾部;
公共静态链makeHead(@Nonnull Transformer tail){
返回新链(空,尾);
}
公共静态链附加(@Nonnull链头,@Nonnull转换器尾){
返回新链条(头部、尾部);
}
专用链(@可空链头,@非空变压器尾){
这个头=头;
this.tail=tail;
}
公共列表运行(列表输入){
List allResults=new ArrayList();
列表头结果;
if(head==null){
headResult=(列表)输入;
}否则{
水头结果=水头运行(输入);
}
用于(内侧内侧:头枕){
//在这里试试看
allResults.addAll(tail.transform(in));
}
返回所有结果;
}
公共静态void main(字符串[]args){
变压器管道1=新变压器(){
@凌驾
公共列表转换(字符串s){
返回集合.singletonList(Integer.valueOf(s)*3);
}
};
变压器管道2=新变压器(){
@凌驾
公共列表转换(整数s){
返回Collections.singletonList(s.longValue()*5);
}
};
变压器管道3=新变压器(){
@凌驾
公共列表转换(长s){
返回Collections.singletonList(新的biginger(String.valueOf(s*7));
}
};
Chain chain1=Chain.makeHead(管道1);
Chain chain2=Chain.append(chain1,pipe2);
Chain chain3=Chain.append(chain2,pipe3);
列表结果=chain3.run(Collections.singletonList(“1”);
系统输出打印项次(结果);
}
}
类型擦除可能会使完全泛型函数的使用更加困难。JVM字节编译器会将类型从泛型类的实例中删除,这样列表就变成了列表。您能再解释一下要求吗?无法弄清楚您拥有什么和想要拥有什么。因此,最好的方法是在插入t时尝试确保类型安全吗管道功能对象(如何??)然后就不提上述方法的警告了?试图澄清一下。希望这有帮助。真的很酷,就像所有好的解决方案一样,一旦知道了就很明显:)谢谢你的回答:)我根据你的回答创建了一个添加异步功能和并行执行的。希望它能帮助别人。显然,如果你能使用Java 8,那么你会ant转而使用Streams,或者类似RxJava的东西。