Search code examples
javacollectionsguavaiterator-facade

How to wrap a java.util.Iterator to change the type of object being iterated


I am providing some facade classes for a third-party API and I need to wrap an iterator so that I can replace what is iterated with my own facade object.

Here's a simplified version of my facade class that wraps an API class called Item

class FacadeItem {
    Item item;
    FacadeItem(Item item) {
        this.item = item;
    }
}

The API provides an iterator of this form Iterator<Item>

I need to implement an iterator of this form Iterator<FacadeItem> that is backed by the iterator of the API.

I considered using the ForwardingIterator from the Guava library as follows:

class FacadeItemIterator<FacadeItem> extends ForwardingIterator<Item> {

    final Iterator<Item> delegate; // backing iterator

    FacadeItemIterator(Iterator<Item> delegate) {
        this.delegate = delegate;
    }

    @Override protected Iterator<Item> delegate() {
        return delegate;
    }

    @Override
    public FacadeItem next() {
        return new FacadeItem(super.next());
    }
}

but the Override of next() is not permitted by the compiler because it is expecting the returned type to be Item, not FacadeItem


Solution

  • The Iterator interface isn't that big, so you could just write your own delegating iterator:

    class FacadeIterator implements Iterator<FacadeItem> {
        private final Iterator<Item> delegate; // set in ctor
    
        @Override
        public FacadeItem next() {
            return new FacadeItem(delegate.next());
        }
    
        // the other two methods just delegate straight
    }
    

    and then

    Iterator<FacadeItem> facadeIterator = new FacadeIterator(itemIterator);
    

    If you're using Guava, you can use their Iterators.transform static method to make one of these for you:

    Iterator<FacadeItem> facadeIterator = Iterators.transform(itemIterator,
        /* anon class of Function<Item, FacadeItem> */);
    

    And in Java 1.8, this option becomes really easy:

    Iterator<FacadeItem> facadeIterator
        = Iterators.transform(itemIterator, FacadeItem::new)