2016-07-02 7 views
1

Für etwas Hintergrund, ich arbeite an einigen Framework-Sachen für eine Programmiersprache, die ich entwickle (JVM-Sprache, das ist), und getestet einige der Framework mit Java-Klassen, daher all die seltsamen Wrapper unten.Erhalte generische Parameter eines generischen Typs


Also, meine Fragen ist, wie bekomme ich die Typvariablen der Grenzen eines Typparameters? Zur Zeit habe ich folgendes:

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

Aber das scheitert mit einem Stackoverflow, wenn es so etwas wie dies trifft:

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

Da es nur weiter versuchen, die Parameter des Typs A über bekommen und über . Ich bin nicht in der Lage, eine Methode zu finden, um die Typvariablen der Typvariablen zu bekommen ... Ich habe versucht, mit getGenericDeclaration herumzuspielen, aber es scheint nicht das zurückzugeben, was ich brauche. Jede Hilfe wird sehr geschätzt.

+2

Sie sollten auch berücksichtigen, Fall 'A >' und 'B >'. Um Zyklen zu vermeiden, sollten Sie bereits bearbeitete und aktuell bearbeitete Typen behalten. Wenn Sie auf einen Typ stoßen, den Sie verarbeiten, aber nicht beenden, sollten Sie einen Platzhalter verwenden und nach der Verarbeitung aller Typen Platzhalter durch korrekte Ergebnisse ersetzen. – csharpfolk

+0

Ja, ich habe versucht, so etwas mit einer Karte zu machen, aber ich bin immer noch nicht genau, wie ich die Informationen bekomme, die ich brauche. –

Antwort

1

@csharpfolk war richtig zu empfehlen, eine Reihe von dem, was bereits geparst wurde und halten Hebelwirkung davon. Unten ist ein kompilierbares & Beispiel, das zeigt, wie das in der Praxis für Ihr Problem aussieht.

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

Ausgang:

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

Sie könnten den Druck reinigen bis zu etwas weniger überflüssig aussehen durch die Art Druck eher als die Generika direkt. Aber das zeigt alle hervorstechenden Merkmale der Lösung.

+0

** Hinweis bearbeiten **: Bereinigt ein wenig Drucken – augray

+0

Vielen Dank! Das ist im Grunde identisch mit dem, was ich getan habe. –