Как получить общий безопасный тип Iterble из Iterable другого типа с помощью преобразователя? (Ява 7)

В нашем проекте мы используем API базы данных на основе прокси (Tinkerpop Frames), поэтому у нас много циклов, таких как:

    List<Link> links = new LinkedList<>();
    for (LinkModel model : obj.getLinks())
    {
        Link l = new Link(model.getLink(), model.getDescription());
        links.add(l);
    }

Я хотел бы избавиться от них по двум причинам:

  1. Чтобы удалить шаблонный код
  2. Для больших списков могут возникнуть проблемы с памятью.

Есть ли хороший способ получить Iterable, который берет от другого и преобразует с использованием данного метода? Что я хотел бы сделать, так это:

Iterable<Link> links_ = new IterableConverter<LinkModel, Link>(obj.getLinks()){
    public Link from(LinkModel m){ return new Link(m.getLink(), m.getDescription()); }
};

Я думаю, в Java 8 есть что-то подобное. Мне нужно это для Java 7.


person Ondra Žižka    schedule 20.04.2016    source источник
comment
Гуава Iterables.transform()?   -  person Andy Turner    schedule 21.04.2016


Ответы (1)


Я потратил некоторое время на борьбу с дженериками, и вот результат, который, кажется, работает нормально:

import java.util.Iterator;

/**
 * An Iterable that takes from the other Iterable and converts the items using given method.
 * The primary reason is to prevent memory issues which may arise with larger lists.
 *
 *  @author Ondrej Zizka, ozizka at redhat.com
 */
public abstract class IterableConverter<TFrom, TTo> implements Iterable<TTo>, Converter<TFrom, TTo>
{
    final Iterable<TFrom> sourceIterable;

    public IterableConverter(Iterable<TFrom> sourceIterable)
    {
        this.sourceIterable = sourceIterable;
    }

    public abstract TTo from(TFrom m);


    @Override
    public Iterator<TTo> iterator()
    {
        return new IteratorBacked<TFrom, TTo>(sourceIterable.iterator(), this);
    }

    class IteratorBacked<TFromX extends TFrom, TToX extends TTo> implements Iterator<TToX> {

        private final Iterator<TFromX> backIterator;
        private final Converter<TFromX, TToX> converter;

        public IteratorBacked(Iterator<TFromX> backIterator, Converter<TFromX, TToX> converter)
        {
            this.backIterator = backIterator;
            this.converter = converter;
        }

        @Override
        public boolean hasNext()
        {
            return backIterator.hasNext();
        }


        @Override
        public TToX next()
        {
            return converter.from(backIterator.next());
        }

    }
}

interface Converter<TFromC, TToC> {
    TToC from(TFromC m);
}
person Ondra Žižka    schedule 20.04.2016
comment
Это выглядит достаточно похоже на то, как работает Iterables.transform в Guava (логически), за исключением того, что для предоставления метода преобразования используется композиция, а не наследование (что является гораздо лучшим подходом, поскольку вы можете повторно использовать экземпляры Converter в нескольких преобразованных итерациях). - person Andy Turner; 21.04.2016
comment
И обратите внимание: поскольку вы сделали IteratorBacked вложенным классом, вам не нужно дублировать такие вещи, как ссылка converter, а дополнительные переменные типа являются излишними. - person Andy Turner; 21.04.2016
comment
Собственно, изначально он у меня не дублировался, но движок дженериков этим не устраивал. Он внутренне превращает одни и те же имена в TTo#1 и TTo#2, и дело пошло не так. Может быть, я мог бы сделать это лучше таким образом. Но API будет таким же. - person Ondra Žižka; 21.04.2016
comment
Что касается композиции и наследования — в моем случае наследование лучше, потому что оно позволяет использовать более короткий код, а преобразования обычно уникальны. - person Ondra Žižka; 21.04.2016