tags:

views:

162

answers:

7

I have a generic PostProcessor interface that looks like this:

public interface PostProcessor<T> {
    public void process( T t );
}

I'm implementing a PostProcessor for generic lists that will call an appropriate postprocessor for each item in the list.

public class ListPostProcessor implements PostProcessor<List<?>> {
    public void process(List<?> t) {
        final PostProcessor<?> p = ...;

        for( Object o : t )
            if(p!=null) 
                p.process(o); // BUG can't pass type Object to process()
    }

}

Ignoring the magic that sets p (signified with ...), my problem is that I can't call process(o) because o is an Object, not a ?. But I can't cast o to the correct type because I don't know what that type is at compile time.

How can I invoke the process() method on each item in the list without having to instantiate a separate ListPostProcessor for every possible set of Types it might be used on?

UPDATE

Following a suggestion from flicken below, I tried using <? extends Object>:

public class ListPostProcessor implements PostProcessor<List<? extends Object>> {
    public void process(List<? extends Object> list) {
        final PostProcessor<? extends Object> p = ... ;

        if( p!=null )
            for( Object o : list )
                p.process(o);  // BUG still a problem
    }
}

But I get the same error from Eclipse:

The method process(capture#3-of ? extends Object) in the type PostProcessor<capture#3-of ? extends Object> is not applicable for the arguments (Object)

+1  A: 

instead of Object, use an Interface called processable which declares process() method and which your list members must be children of

DVK
Thanks for the suggestion DVK. Unfortunately, that's not going to fly -- I don't necessarily have the ability to modify the type of the objects I'm processing.
Mike
+1  A: 

Use < ? extends Object>

 public class ListPostProcessor implements PostProcessor<List<? extends Object>> {
      ...
 }
flicken
Hm, that seemed like a great idea, but it didn't work. See edit above.
Mike
+2  A: 

To be consistent in your Generic typing, you can specify your PostProcessor in terms of a single type that all of your specification adheres to:

   public interface PostProcessor<U> {
      public void process( U u );
   }

   public class ListPostProcessor<T> implements PostProcessor<List<T>> {
      public void process(List<T> t) {
        final PostProcessor<T> p = ...;
        for (T o : t) {
            if (p != null) {
              p.process(o);
            }
         }
      }
   }

If you want to have a single ListPostProcessor instance that can process any sort of Object, you can instantiate it as deep/shallow in the class hierarchy as is necessary:

new ListPostProcessor<MyBaseClass>();

will do the trick. As will:

new ListPostPorcessor<Object>();

The latter will be the similar to not using Generics at all, and it allows you to do what you are requesting - allow for any type of Object to be processed by a Genericized class.

akf
Thank akf. Unfortunately that means I would have to instantiate a separate ListPostProcessor for every type of object I want to process, which is something I was hoping to avoid (see original problem description). Cheers, Mike
Mike
Mike, see my edit, which might address your concerns.
akf
Hm, good point. Although I might as well not bother parameterizing ListPostProcessor then. Which leads me to what I think is the right solution: `public class ListPostProcessor implements PostProcessor<List<Object>>`
Mike
A: 

I noticed that this is no C# code, but would there be a way to use reflection, would it be a good application for reflection here?

Will Marcouiller
That is one solution I was thinking about employing, but it's a bit ugly and slow. It SEEMS like there should be a plain generics way of handling this situation, but it's elusive so far.
Mike
A: 

Actually, on further reflection what seems like the best solution is to do the following:

public class ListPostProcessor implements PostProcessor<List<?>> {
    @SuppressWarnings("unchecked")
    public void process(List<?> t) {
        final PostProcessor p = ...;

        for( Object o : t )
            if(p!=null) 
                p.process(o);
    }

}

The previous best answer (akf's suggestion of using ListPostProcessor implements PostProcessor<List<Object>> discarded the type of List, which I actually needed in order to find the appropriate PostProcessor for p. This solution keeps that type information.

Mike
A: 

I was going for a series of comments but the more I look at it the less I think that this will work at all... ever... at least not in the present form. There are two major problems in your proposal :

1- Erasure generics. The processor is a generic class that is instantiated for a specific type to be processed. As long as you feed it that type you are good to go. On the other hand the ListPostProcessor is declared to be a processor type that handles lists of anything but turns around and wishes to get the exact required processor type for the list content. No amount of generics acrobatics will allow you do do that, partly because Java generics are erased after compilation leaving just the raw type. Therefore all the generics declaration are gone once the program is running.

2- Rutime Type routing : this is the part that hurts: What mechanics decides how the data is routed to the appropriate processor ? Do you have processors for all possible types or all possible types that extend Object ? How is it supposed to know what processor to get ? What does a processor do anyways ? I would think that one would create a Processor, extending the interface for a specific type or type structure and use the automatic type casting generics provide to start working on the appropriate type right away. In the case of the ListPostProcessor it has no idea what processor type to call, firstly because generics type information are erased but also you would require some sort of factory or dispatcher in addition to the ListPostProcessor to call the right processor.

3- Even if all of the above finally worked how is a single PostProcessor supposed to handle all the objects of a list of anything. The only way would be to instantiate a PostProcesor of Object Therefore this code would work but I would suppose not really what you are after :

  public class ListPostProcessor implements PostProcessor<List<? extends Object>>{
 public void process(List<?> t){
  final PostProcessor<Object> p = new PostProcessor<Object>(){
   public void process(Object o){
     // do stuff
     }
        };
  for (Object o : t){
   if (p != null){
                        p.process(o);
   }
  }
 }
  }

Or if you really want to process each item with it's own type then you need some magic and the PostProcessor would need to be inside the loop. Like so :

public class ListPostProcessor implements PostProcessor<List<? extends Object>>{
 public void process(List<?> t){
  for (Object o : t){
   if (p != null){
    PostProcessor<? extends Object> p = ProcessorFactory.getAppropriateProcessor(o); 
    p.process(o);
   }
  }
 }
}
Newtopian
Great comments, Newtopian. I think they're all valid, but what you don't know is that I have some magic up my sleeve to address them. Regarding #1, I'm using Guice to instantiate my list processor, and Guice gives me a ParameterizedType that tells me the child type, which I can then use to get the appropriate processor for that child type. For concern #2, I left out a part of the interface which wasn't critical to the original problem, namely a method called `boolean PostProcess.canProcess(Object o)`. And for #3, each item in the list is of the same type, so one PP for all is fine.
Mike
A: 

if(object instanceof type) ....

Oh Great Typing Monster, Hidden Nemesis of Prototyping - shame thyself into obscurity and leave this pristine world, go unto Forbidden Planet and there munch upon the plants there. They are sweet and you will like them.

Never happen, either you live with the Giant Switch or go to auto-vivification. ( auto-vilification )

I saw this early in my Java studies, having been there before I just wrote my Exception mechanism on a giant switch.

Still using it today.

Nicholas Jordan