views:

118

answers:

6

Hi fellas, Java noob here,

I have an Iterator<TypeA> that I want to convert to Iterator<TypeB>. TypeA and TypeB cannot be directly cast to each other but I can write a rule how to cast them. How can I accomplish this? Should I extend and override Iterator<TypeA>'s next, hasNext and remove methods?

Thanks.

+5  A: 

Write an adapter. Here's an example in Java. OReilly's book 'Head First: Design Patterns' gives the best explanation on the topic.

Boris Pavlović
+1  A: 

Since Generics in Java is just a compile time construct, I feel you'll be better off casting each element you retrieve on invoking next() than to cast the iterators.

anirvan
+4  A: 

I guess you should write your own iterator(it can implements java.util.iterator) with your ((converting)) rule in a method and use it.

Aboelnour
That was my answer too :) Write an Iterator<TypeB> that takes an Iterator<TypeA> as constructor argument, and then convert the objects as you go, in the next() method.
Michael Clerx
+2  A: 

You can use the following snippet :

public static void main(String... args){
    Iterator<TypeA> iteratorTypeA = methodToGetIteratorTypeA();
    Iterator<TypeB> iteratorTypeB = new IteratorConveter<TypeA, TypeB>(iteratorTypeA,
        new Converter<TypeA, TypeB>(){
            public TypeB convert(TypeA typeA){
                return null; //Something to convert typeA to type B
            }
        });
}

public class IteratorConveter<F, T> implements Iterator<T> {
    private final Converter<? super F, ? extends T> converter;
    private final Iterator<F> iterator;

    public IteratorConveter(Iterator<F> iterator, Converter<? super F, ? extends T> converter) {
        this.converter = converter;
        this.iterator = iterator;
    }

    public boolean hasNext() {
        return iterator.hasNext();
    }

    public T next() {
        return converter.convert(iterator.next());
    }

    public void remove() {
        iterator.remove();
    }
}


interface Converter<F, T> {
    T convert(F object);
}
Colin Hebert
Sorry I'm not stupid just a bit rushed, in the conversion part, I'm instantiating a new instance of TypeB and returning it. Something like: return new DictionaryEntry(doc.get("name").toString(), doc.get("id").toString()); but I'm running out of heap space, meaning these instances are not released. The Iterator<TypeA> is coming from a DB interface so it does not hold more than a few instances at a time in memory (I presume). Am I doing something stupid or it all depends on TypeB and it's iterator? Thanks.
parsa28
+1  A: 

It might be a good idea to have a super-class which they share, but that depends on how exactly those two types are defined, or possibly even have one of them inherit from the other, as it seems they are two representations for the same data. That would also solve your iterator issues.

Or you could write a container which internally uses one of them for data access and has a sleek representation to the outside world. I refer to "Composition over Inheritance".

But in essence, it all comes down to: Have you thought very hard about your class structure? Are you sure it's as good as it can be?

Kdansky
+6  A: 

You don't need to write this yourself. Guava (formerly Google-collections) has you covered in Iterators.transform(...)

You provide your iterator and a function that converts TypeA to TypeB and you're done.

Iterator<TypeA> iterA = ....;
Iterator<TypeB> iterB = Iterators.transform(iterA, new Function<TypeA, TypeB>() {
   @Override
   public TypeB apply(TypeA input) {
     TypeB output = ...;// rules to create TypeB from TypeA
     return output;
   }
});
GaryF