2016-07-02 5 views
1

Pour quelques raisons, je suis en train de développer des frameworks pour un langage de programmation que je suis en train de développer (langage JVM) et je testais une partie du framework avec des classes Java, donc tous les wrappers bizarres ci-dessous. Donc, mes questions sont: comment obtenir les variables de type des limites d'un paramètre de type? Actuellement, je donne les résultats suivants:Obtention des paramètres génériques d'un type générique

public static TemplateGenerics of(Class clazz) { 
    TemplateGenerics generics = new TemplateGenerics(); //TemplateGenerics is a wrapper class for generics that appear in the class header 
    Stream.of(clazz.getTypeParameters()).forEach(typeVariable -> { 
     java.lang.reflect.Type b = typeVariable.getBounds()[0]; 
     try { 
      Class c = Primitives.resolveClass(b.getTypeName().split("<", 2)[0]); //Is there a better way to do this? 
      TemplateGenerics sub = TemplateGenerics.of(c); //Recursivley get the generics - it fails here 
      generics.getConditionals().add(new Conditional(new Type.Hierarchical(sub, c.getName()), Conditional.Condition.EXTENDS, typeVariable.getName())); //Conditional is another wrapper class that handles bounds of the generic, 
                                          //Type.Hierachical is yet another wrapper class that wraps types that appear in class headers 
     } catch (ClassNotFoundException e) { 
      throw new RuntimeException(e); //For testing purposes 
     } 
    }); 
    return generics; 
} 

Mais cette tentative échoue avec un StackOverflowException lorsqu'il rencontre quelque chose comme ceci:

public class A<T extends A<T>> ...

Comme il continue à essayer d'obtenir les paramètres de type A plusieurs reprises . J'ai été incapable de trouver une méthode pour obtenir les variables de type de la variable type ... J'ai essayé de jouer avec getGenericDeclaration, mais il ne semble pas retourner ce dont j'ai besoin. Toute aide est grandement appréciée.

+2

Vous devez également prendre en compte le cas 'A >' et 'B >'. Généralement, pour éviter les cycles, vous devez conserver les types déjà traités et actuellement traités. Si vous rencontrez un type que vous commencez à traiter mais ne terminez pas, vous devez utiliser un espace réservé et après avoir traité tous les types, vous devez remplacer les espaces réservés par des résultats corrects – csharpfolk

+0

Oui, j'ai essayé de faire quelque chose avec une carte, mais je suis toujours Je ne sais pas exactement comment obtenir l'information dont j'ai besoin. –

Répondre

1

@csharpfolk avait raison de suggérer de garder une trace de ce qui a déjà été analysé et de tirer parti de cela. Ci-dessous est un exemple de runnable compilable qui montre à quoi cela ressemble en pratique pour votre problème.

package so.answers; 

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import java.util.stream.Stream; 

public class TemplateGenerics { 

    private final List<Conditional> conditionals = new ArrayList<>(); 

    public List<Conditional> getConditionals(){ 
     return conditionals; 
    } 

    public String toString(){ 
     return getConditionals().toString(); 
    } 

    public static TemplateGenerics of(Class<?> clazz) { 
     return TemplateGenerics.of(clazz, new HashMap<>()); 
    } 

    private static TemplateGenerics of(Class<?> clazz, Map<Class<?>, TemplateGenerics> existingGenericsForClasses) { 
     if(existingGenericsForClasses.containsKey(clazz)){ 
      return existingGenericsForClasses.get(clazz); 
     } 
     final TemplateGenerics generics = new TemplateGenerics(); 
     existingGenericsForClasses.put(clazz, generics); 

     Stream.of(clazz.getTypeParameters()).forEach(typeVariable -> { 
      java.lang.reflect.Type b = typeVariable.getBounds()[0]; 
      try { 
       Class<?> c = Primitives.resolveClass(b.getTypeName().split("<", 2)[0]); //Is there a better way to do this? 
       TemplateGenerics sub = TemplateGenerics.of(c, existingGenericsForClasses); //Recursivley get the generics - it fails here 
       generics.getConditionals().add(new Conditional(new Type.Hierarchical(sub, c.getName()), Conditional.Condition.EXTENDS, typeVariable.getName())); //Conditional is another wrapper class that handles bounds of the generic, 
                                           //Type.Hierachical is yet another wrapper class that wraps types that appear in class headers 
      } catch (ClassNotFoundException e) { 
       throw new RuntimeException(e); //For testing purposes 
      } 
     }); 
     return generics; 
    } 

    public static class Conditional{ 
     public static enum Condition{ 
      EXTENDS, 
      SUPER 
     } 

     private final Type.Hierarchical hierarchical; 
     private final Condition condition; 
     private final String typeName; 

     public Conditional(Type.Hierarchical hierarchical, Condition condition, String typeName){ 
      this.hierarchical = hierarchical; 
      this.condition = condition; 
      this.typeName = typeName; 
     } 

     public String toString(){ 
      return "Conditional$typeName="+typeName+" " 
        +"Conditional$condition="+condition+" " 
        +"Conditional$hierarchical={"+hierarchical+"} ";      
     } 
    } 

    public static class Primitives{ 
     public static Class<?> resolveClass(String name) throws ClassNotFoundException{ 
      String trimmedName = name.replaceFirst(TemplateGenerics.class.getCanonicalName()+".", ""); 

      //not sure why this nonsense with the trimmed name 
      //is necessary, but you seem to already have a better 
      //version of this method anyway 
      if(trimmedName.contains(TemplateGenerics.class.getCanonicalName())){ 
       name = trimmedName; 
      } 
      return Primitives.class.getClassLoader().loadClass(name); 
     } 
    } 

    public static class Type{ 
     public static class Hierarchical{ 
      private TemplateGenerics generics; 
      private String name; 

      public Hierarchical(TemplateGenerics generics, String name){ 
       this.generics = generics; 
       this.name = name; 
      } 


      private boolean printing; 

      public String toString(){ 
       try{ 
        if(!printing){ 
         printing = true; 
         return "Hierarchical$name="+name+ " Hierarchical$generics=("+generics+")"; 
        } else { 
         return "Hierarchical$name="+name; 
        } 
       } finally { 
        printing = false; 
       } 
      } 
     } 
    } 

    public static class B{ 

    } 

    public static class C<T extends B>{ 

    } 

    public static class A<T extends A<T>>{ 

    } 

    public static class X<T extends Y>{ 

    } 

    public static class Y<T extends X>{ 

    } 

    public static void main(String[] args){ 
     System.out.println("For A:"+TemplateGenerics.of(A.class)); 
     System.out.println("For C:"+TemplateGenerics.of(C.class)); 
     System.out.println("For X:"+TemplateGenerics.of(X.class)); 
    } 
} 

Sortie:

For A:[Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$A Hierarchical$generics=([Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$A} ])} ] 
For C:[Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$B Hierarchical$generics=([])} ] 
For X:[Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$Y Hierarchical$generics=([Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$X Hierarchical$generics=([Conditional$typeName=T Conditional$condition=EXTENDS Conditional$hierarchical={Hierarchical$name=so.answers.TemplateGenerics$Y} ])} ])} ] 

Vous pouvez nettoyer l'impression de regarder un peu moins redondant en imprimant le type plutôt que les médicaments génériques directement. Mais cela montre toutes les caractéristiques saillantes de la solution.

+0

** Remarque modifier **: Nettoyé impression un peu – augray

+0

Merci beaucoup! C'est fondamentalement identique à ce que j'ai fini par faire. –