DrewEaster
DrewEaster

Reputation: 3426

Java Generics Issue (w/ Spring)

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

Upvotes: 4

Views: 810

Answers (2)

axtavt
axtavt

Reputation: 242686

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);
            }
        }
    }
}

Upvotes: 1

bmargulies
bmargulies

Reputation: 100013

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.

Upvotes: 3

Related Questions