Reputation: 3426
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
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
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