2009-10-02 5 views
3

Donc, le groupe avec lequel je travaille a réduit la quantité de code que nous devons taper pour certaines choses. Dans ce cas, une page Web Spring affichant une liste utilisant les bibliothèques DisplayTag. La façon dont cela est fait est avec une classe utilisant des génériques qui étendent l'objet Controller, puis une sous-classe pour chaque page sur laquelle il devrait fonctionner.Comment faire pour supprimer Type Erasure en Java

Ce contrôleur affiche SystemErrorReport et définit le type à SystemErrorReportCommand.

public class SystemErrorReportController extends 
    GenericSearchAndSelectController<SystemErrorReportCommand> { 

Le problème est que SystemErrorReportCommand, être passé comme un type, a besoin d'avoir une déclaration manuelle d'elle-même dans son constructeur, comme suit:

public SystemErrorReportCommand() 
{ 
    super(SystemErrorReport.class); 
} 

L'objet de commande est ensuite passé à quelque chose qui a besoin de connaître son type explicite. Sans le spécifier manuellement quelque part, il revient comme GenericCommand, une autre classe de la nôtre, parce que le bit SystemErrorReportCommand est perdu après la compilation.

Je ne suis pas ravi de cela, car il semble que nous puissions automatiser davantage pour réduire les erreurs de développement. Y a-t-il une manière plus élégante de faire ceci, ou suis-je coincé avec ceci à cause de l'effacement de type?

Répondre

1

Peut-être que vous pourriez utiliser quelque chose comme Javassist faire quelques méta-programmation.

Il est utilisé dans Tapestry à cet effet, voir article this.

3

Même après que l'effacement a fait son travail, il est toujours possible de récupérer des informations génériques à partir de: Champ, Méthode (y compris les paramètres de méthode) et Classe en utilisant la réflexion.

Par exemple, il est possible de récupérer le SystemErrorReportCommand de SystemErrorReportController en utilisant

((ParameterizedType) SystemErrorReportController.class.getGenericSuperType()).getActualTypeArguments[0]; 

Pourtant, c'est que le début. Vous devez commencer à utiliser cette information à l'aide de l'API de réflexion, il est donc probable que votre conception en souffrira si vous n'attendiez pas quelque chose comme ça.

Voici un petit exemple de ce que vous pouvez extraire à l'exécution:

public abstract class Test<T extends Number> extends ArrayList<Long> implements Comparable<String>, List<Long>{ 
    public List<String> field; 
    public abstract <M> M method(List<T> arg); 

    public static void main(String... args) throws Exception { 
     TypeVariable<Class<Test>> t = Test.class.getTypeParameters()[0]; 
     Class<?> upperBound = (Class<?>) t.getBounds()[0]; 


     System.out.println("Test<" + t + " extends " + upperBound.getName() + ">"); 
     System.out.println("extends " + Test.class.getGenericSuperclass()); 
     System.out.println("implements " + Arrays.toString(Test.class.getGenericInterfaces())); 
     System.out.println("{"); 
     System.out.println(Test.class.getMethods()[1].toGenericString()); 
     System.out.println(Test.class.getFields()[0].toGenericString()); 
     System.out.println("}"); 
    } 
} 

qui sortie:

Test<T extends java.lang.Number> 
extends java.util.ArrayList<java.lang.Long> 
implements [java.lang.Comparable<java.lang.String>, java.util.List<java.lang.Long>] 
{ 
public abstract <M> M Test.method(java.util.List<T>) 
public java.util.List<java.lang.String> Test.field 
} 

J'utilise la méthode toGenericString(). Cependant, c'est juste une jolie méthode d'impression! Il n'y a pas besoin d'analyser cette chaîne: réflexion api fournit toutes les informations nécessaires, par exemple: Field.getGenericType(), Method.getArgumentTypes ...

+0

Permettez-moi de vérifier ce lundi.Nous réfléchissons déjà beaucoup, je devrais peut-être être plus précis avec ma question, mais je ne veux pas me passer de l'exemple en face de moi. –

+1

Ok, vérifié. Le problème ici est qu'il y a quelque chose comme: classe Foo ; -et- la classe Baz s'étend Bar; -et- la classe Zab s'étend Bar; Quand quelque chose appelle Foo avec Baz (sous-classe de Bar), tout ce que je peux obtenir est Bar, pas Baz; J'ai perdu la sous-classe et ne peux récupérer que le type explicite. Cela avait-il un sens? –

+0

Oui, malheureusement, c'est le genre d'information qui est effacée. Au moment de l'exécution, il n'y a qu'une seule instance de la classe Foo (Foo.class). Donc, vous obtenez seulement le type générique "déclaré" plutôt que le type générique "instance". C'est ce sur quoi porte la discussion "reified generic" (http://gafter.blogspot.com/2006/11/reified-generics-for-java.html). Malheureusement, cela ne fera pas partie de Java 7 non plus. – vdr

4

Contrairement à ce qui est largement accepté et rarement connu, l'effacement de type peut être évité, ce qui signifie que l'appelé a la capacité de savoir quels paramètres génériques ont été utilisés pendant l'appel.

S'il vous plaît jeter un oeil à: Using TypeTokens to retrieve generic parameters

Merci

Questions connexes