簡體   English   中英

Java Generics:將通用函數對象鏈接在一起

[英]Java Generics: chaining together generic function object

我一直在努力解決以下問題。 我有一系列函數對象,每個函數對象都有自己的輸入和輸出類型,通過java中的泛型類型參數定義。 我想在鏈中安排這些,以便將原始數據輸入到第一個函數,轉換為輸出類型,這是下一個對象的輸入類型,依此類推。 當然這對於硬編碼來說是微不足道的,但是我想讓代碼可以插入到新的函數對象中。 如果我只是省略類型參數(只有最終的輸出類型),這就是事情的樣子:

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

這里,數據上的迭代器在函數對象之間傳遞,上下文應該是Context。 有沒有辦法保持以下類型的管道可插拔,仍然保持類型安全?

編輯:為了清楚起見,我有一系列功能對象,管道。 每個都將特定類型作為輸入,並輸出另一種類型。 (實際上是這些類型的迭代器)這些將被鏈接在一起,例如, Pipe<A,B> -> Pipe<B,C> -> Pipe<C,D> -> ... ,以便輸出一個pipe是下一個管道的輸入類型。 這里還有一個源,它輸出一個類型為A的迭代器,以及一個接受類型的接收器(過去管道的輸出)。 這會讓事情更清楚嗎? 問題是,因為輸入和輸出類型的兼容性存在嚴重依賴性,有沒有辦法確保這一點?

我開始認為將函數對象插入管道可能是確保類型安全的最佳時間,但我不知道如何做到這一點。

編輯2:我有一個函數對象的加法器方法,如下所示:

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

我想檢查第一個類型參數是否與當前管道的“結束”相同,如果沒有則拋出異常? 我不認為這里有一個很好的方法來獲得編譯時安全性。 然后可以將當前管道的“結束”設置為輸入管道的第二個類型參數。 我想不出如何用泛型來做這件事,並且傳遞類信息似乎很可怕。

這是一種方法。 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);
    }
}

這是另一種方法:這種方式允許轉換步驟以產生列表。 例如,轉換可以將字符串拆分為多個子字符串。 此外,如果轉換任何值產生異常,它允許常見的異常處理代碼。 它還允許使用空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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM