2010-06-03 2 views
4

Je pense que je peux être victime d'un effacement de type, mais j'ai pensé que je verrais d'abord avec les autres ici.Java Generics Issue (avec Spring)

J'ai l'obligation de faire quelque chose comme ceci:

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

Il est parfaitement valide pour écrire ce code. Cependant, j'essaye d'implémenter cette fonctionnalité en utilisant un ressort BeanFactory et je ne peux pas le faire.

Ce que je voudrais faire est ce ...

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

Comme vous pouvez le voir, je l'ai mis dans ???????? où je voudrais récupérer un haricot de type Foo<T>, où T étend Bar. Cependant, il n'est pas possible de dériver un objet Class de type Foo<T> et donc je suppose que ce que j'essaie de faire est impossible?

Quelqu'un d'autre voit un moyen de contourner cela ou une autre façon de mettre en œuvre ce que je suis en train de faire?

Merci,

Andrew

+0

À quoi sert le paramètre 'clazz'? –

+0

Pourquoi essayez-vous d'utiliser une usine de haricots? Vous pouvez certainement utiliser la configuration Spring standard pour y parvenir? –

+0

@Rob utilise parfois des usines de haricots pour obtenir un contrôle plus fin de la durée de vie que ce que vous pouvez obtenir en contrôlant simplement la paresse, ou pour profiter d'un prototype pour obtenir plusieurs instances. – bmargulies

Répondre

1

Puisque vous ne pouvez pas définir les haricots de type Foo<T> avec T spécialisé dans le contexte de printemps, je suppose que vous avez en fait des sous-classes de 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" /> 

Dans ce cas, vous pouvez utiliser le fait que les paramètres de type ne sont pas effacés de la définition de la superclasse:

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

J'avais aussi élaboré quelque chose dans ce sens. Merci pour la contribution. – DrewEaster

3

Oui, cela est une situation d'effacement de type. Puisque vous ne pouvez pas obtenir un Class pour Foo<T>, vous devez travailler avec Foo et supprimer l'avertissement.

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

Vous trouverez peut-être this file intéressant - il est une classe utilitaire avec des avertissements que Apache CXF supprimée utilise pour centraliser tous ces incidents malheureux.

Bien sûr, tout ceci suppose que votre config XML (ou autre) aboutira à un Foo utilisable.

+0

+1 pour répondre avant que j'aie eu une chance, une note cependant, le paramètre clazz n'est pas nécessaire. –

+0

@Et êtes-vous sûr? Je pensais qu'il était nécessaire de dire à javac ce que T est pour l'invocation particulière, car il n'y a aucun objet de T ou quelque chose dans la liste des paramètres. J'avoue que je suis spongieux sur le sujet, n'hésitez pas à éditer la réponse si je suis confus. – bmargulies

+0

Eh bien, si vous faites quelque chose comme 'Foo var = fooFactoryImpl.createFoo()', le compilateur déduit l'heure de la variable (dans ce cas Bar). Et si vous voulez utiliser un type spécifique, vous pouvez toujours appeler 'fooFactoryImpl. createFoo();'. –