views:

77

answers:

2

I think I may be a victim of type erasure but thought I'd check with others here first.

I have the requirement to do something like this:

public interface FooFactory {
   public <T extends Bar> Foo<T> createFoo( Class<T> clazz );
}

It is perfectly valid to write this code. However, I'm trying to implement this functionality using a Spring BeanFactory and I can't do it.

What I'd like to do is this...

public class FooFactoryImpl implements BeanFactoryAware {
   private BeanFactory beanFactory;

   public <T extends Bar> Foo<T> createFoo( Class<T> clazz ) {
      return beanFactory.getBean( ????????? );
   }

   public void setBeanFactory( BeanFactory beanFactory ) {
      this.beanFactory = beanFactory; 
   }
}

As you can see, I've put in ???????? where I'd like to retrieve a bean of type Foo<T>, where T extends Bar. However, it is not possible to derive a Class object of type Foo<T> and so I assume what I'm trying to do is impossible?

Anyone else see a way round this or an alternative way of implementing what I'm trying to do?

Thanks,

Andrew

+3  A: 

Yes, this is a type erasure situation. Since you can't get a Class for Foo<T>, you have to work with Foo and suppress the warning.

@SuppressWarnings("unchecked")
public <T extends Bar> Foo<T> createFoo( Class<T> clazz ) {
      return (Foo<T>) beanFactory.getBean("Name of Bean", Foo.class);
}

You might find this file interesting -- it's a utility class with warnings suppressed that Apache CXF uses to centralize all these unfortunate incidents.

Of course, all this assumes that your XML (or whatever) config will result in a usable Foo.

bmargulies
+1 for answering before I had a chance, one note though, the parameter clazz is not needed.
Andrei Fierbinteanu
@And are you sure? I thought it was needed to tell javac what T is for the particular invocation, since there's no object of T or something<T> in the parameter list. I admit that I'm squishy on the topic, feel free to edit the answer if I'm confused.
bmargulies
Well if you do something like `Foo<Bar> var = fooFactoryImpl.createFoo()`, the compiler infers the time from the variable (in this case Bar). And if you want to use a specific type you could always call `fooFactoryImpl.<someTypeThatExtendsFoo>createFoo();` .
Andrei Fierbinteanu
For example if you have `public <T> T method(Foo<T> foo) {//code}` and try to call it like `TypeThatExtendsBar t = method(fooFactoryImpl.<TypeThatExtendsBar>createFoo());` which won't work without the specific parameter - not giving the parameter implies the method returns Bar)
Andrei Fierbinteanu
Thanks for the input, everyone. An interesting discussion for sure!
drewzilla
+1  A: 

Since you can't define a beans of type Foo<T> with specialized T in Spring context, i guess that you actually have subclasses of Foo<T>:

abstract public class Foo<T> { ... }

public class FooString extends Foo<String> { ... }
public class FooInteger extends Foo<String> { ... }

-

<bean id = "fooInteger" class = "FooInteger" />
<bean id = "fooString" class = "FooString" />

In this case you can use the fact that type parameters are not erased from superclass definition:

public class FooFactory implements ApplicationContextAware {

    private Map<Class<?>, Foo<?>> beans = new HashMap<Class<?>, Foo<?>>();

    @SuppressWarnings("unchecked")
    public <T> Foo<T> createFoo(Class<T> c) {
        return (Foo<T>) beans.get(c);
    }

    @SuppressWarnings("unchecked")
    public void setApplicationContext(ApplicationContext ctx)
            throws BeansException {

        Collection<Foo> candidates = ctx.getBeansOfType(Foo.class).values();
        for (Foo candidate: candidates) {
            Type superclass = candidate.getClass().getGenericSuperclass();
            if (superclass instanceof ParameterizedType) {
                ParameterizedType t = (ParameterizedType) superclass;
                Class<?> p = (Class<?>) t.getActualTypeArguments()[0];
                beans.put(p, candidate);
            }
        }
    }
}
axtavt
I had worked out something along these lines too. Thanks for the input.
drewzilla