Java Generics: связывание воедино универсального объекта функции - PullRequest
6 голосов
/ 30 декабря 2011

Я боролся со следующей проблемой.У меня есть ряд функциональных объектов, каждый из которых имеет свои собственные типы ввода и вывода, определенные с помощью аргументов универсального типа в Java.Я хотел бы расположить их в цепочке так, чтобы исходные данные вводились в первую функцию, преобразовывались в тип вывода, который является типом ввода следующего объекта, и так далее.конечно, это было бы тривиально для жесткого кода, но я бы хотел, чтобы код был подключаем к новым объектам функций.если я просто опускаю аргументы типа (только конечный тип вывода), то вот как все это выглядит:

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

здесь итератор данных передается между объектами функции, а контекст должен быть Context.Есть ли способ, чтобы следующий тип трубы был вставным и при этом сохранял безопасность типа?

edit: для ясности, у меня есть ряд функциональных объектов, каналов.каждый принимает в качестве входных данных определенный тип и выводит другой тип.(на самом деле итераторы для этих типов) они будут объединены в цепочку, например, Pipe<A,B> -> Pipe<B,C> -> Pipe<C,D> -> ..., так что выходные данные одного канала являются типом ввода для следующего канала.Здесь также есть источник, который выводит итератор типа A, и приемник, который будет принимать тип (вывод предыдущего канала).это проясняет ситуацию?Вопрос в том, что существует критическая зависимость от совместимости типов ввода и вывода, есть ли способ обеспечить это?

Я начинаю думать, что при вставке объектов функции в конвейер может быть лучшее время для обеспечения безопасности типов, но я не уверен, как это сделать.

edit 2: у меня есть метод сумматора для функциональных объектов, который в настоящее время выглядит следующим образом:

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

Я хотел бы проверить, совпадает ли первый параметр типа с параметром "конец "текущей трубы, и выбросить исключение, если нет?я не думаю, что есть хороший способ обеспечить безопасность времени компиляции здесь.тогда «конец» текущей трубы может быть установлен на параметр второго типа входной трубы.Я не могу придумать, как сделать это с помощью дженериков, и передача информации о классе кажется довольно отвратительной.

Ответы [ 2 ]

10 голосов
/ 30 декабря 2011

Вот способ сделать это.Метод 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);
    }
}
0 голосов
/ 08 апреля 2014

Вот еще один способ сделать это: этот способ позволяет сделать шаг преобразования в виде списка. Например, преобразование может разбить строку на несколько подстрок. Более того, он допускает общий код обработки исключений, если преобразование любого из значений приводит к исключению. Это также позволяет использовать пустой список в качестве возвращаемого значения вместо неоднозначного нулевого значения, которое необходимо проверить, чтобы избежать исключения 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...