2016-09-08 1 views
5

java.lang.reflect.Type.toString() Aufruf bietet sehr schöne Darstellung von generischen Typen erstellen:Class.forName Äquivalent für ParameterizedType der von String

@Test 
public void typeToStringWithTypeToken() { 
    assertEquals("java.util.List<java.lang.String>", new TypeToken<List<String>>() {}.getType().toString()); 
} 

Was ich brauche, ist die Umkehrung der Type.toString() Verfahren, dh ein Verfahren, das Type s aus gegebenen Zeichenfolge erstellen Darstellung:

public static Type parseTypeString(String typeString) throws ClassNotFoundException { 
    if (typeString.indexOf('<') == -1) { 
     return Class.forName(typeString); 
    } 
    // ??? how to implement rest 
    throw new IllegalStateException("not implemented"); 
} 

Welche folgenden Tests mit spezialisierten Arten passieren kann:

@Test 
public void justSimpleClassName() throws Exception { 
    assertEquals(Integer.class, parseTypeString("java.lang.Integer")); 
} 

@Test 
public void listOfInteger() throws Exception { 
    assertEquals(new TypeToken<List<Integer>>() {}.getType(), parseTypeString("java.util.List<java.lang.Integer>")); 
} 

@Test 
public void complexType() throws Exception { 
    assertEquals(new TypeToken<Map<List<Integer>, Set<String>>>() {}.getType(), parseTypeString("java.util.Map<java.util.List<java.lang.Integer>, java.util.Set<java.lang.String>>")); 
} 

Ich konnte keine Bibliothek oder eine SO-Frage finden, die dieses Problem behebt.

Antwort

2

Nun ... Sie können es selbst tun, zum Beispiel mit antlr4 folgende Grammatik

type returns[ClassBuilder value] 
    : cls=CLASS   { $value = ClassBuilder.parse($cls.text); } 
    | cls=CLASS   { $value = ClassBuilder.parse($cls.text); } 
     LT head=type  { $value.add($head.value); } 
     (COMMA tail=type { $value.add($tail.value); })* GT 
    ; 

GT : '>' 
    ; 

LT : '<' 
    ; 

COMMA 
    : ',' 
    ; 

CLASS 
    : ('a'..'z'|'A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9'|'$'|'.'|'_')* 
    ; 

Wo ClassBuilder wie

public class ClassBuilder { 
    private final Class<?> clazz; 
    private final List<ClassBuilder> parameters = new ArrayList<>(); 

    public ClassBuilder(final Class<?> clazz) { 
     this.clazz = clazz; 
    } 

    public static ClassBuilder parse(String clazz) { 
     try { 
      return new ClassBuilder(Class.forName(clazz)); 
     } catch (ClassNotFoundException e) { 
      // do better handling here 
      throw new IllegalStateException(e); 
     } 
    } 

    public void add(ClassBuilder builder) { 
     parameters.add(builder); 
    } 

    public Type build() { 
     // class is not parametrized 
     if (parameters.isEmpty()) { 
      return clazz; 
     } 

     return ParameterizedTypeImpl.make(
       clazz, 
       parameters.stream() 
          .map(ClassBuilder::build) 
          .toArray(Type[]::new), 
       null); 
    } 
} 

aussieht und dann analysieren schließlich String

CharStream stream = 
    new ANTLRInputStream(
     "java.util.Map<java.util.List<java.lang.Integer>, java.util.Set<java.lang.String>>" 
    ); 

TokenStream tokenStream = 
    new CommonTokenStream(new ParametrizedTypeLexer(stream)); 
ParametrizedTypeParser parser = 
    new ParametrizedTypeParser(tokenStream); 

assertEquals(
     new TypeToken<Map<List<Integer>, Set<String>>>() {}.getType(), 
     parser.type().value.build() 
); 

Sie können das Arbeitsbeispiel here sehen.

HINWEIS

CLASS Lexer Regel kann ein bisschen ungenau sein. Auch dieser Parser gilt nur für nicht-primitive Klassen und parametrisierte Typen, aber Sie können natürlich auch Wildcard-Typen unterstützen.

+0

Das ist eine gute Antwort! Ich werde es testen und weitere Rückmeldungen geben. Benutzer müssen möglicherweise den kniffligen Teil der Implementierung bemerken, indem sie 'ParameterizedTypeImpl' von der ** privaten ** API verwenden ([ParameterizedTypeImpl.java] (http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/ 687fd7c7986d/src/teilen/Klassen/Sonne/reflektieren/Generika/reflectiveObjects/ParameterizedTypeImpl.java)). Ich denke, Java-Leute versuchen, uns Sterbliche, die schwarze Magie durchführen, durch Bereitstellung einer öffentlichen 'ParameterizedType'-Implementierung zu entmutigen. – b10y

+0

@ b10y afaik gibt es Implementierungen in ParameterizedType in Guave, aber es gibt kein Problem, es von Hand zu implementieren – vsminkov

1

Die Ausgabe von java.lang.reflect.Type.toString() ist implementierungsspezifisch.
Das Verhalten, das Sie sehen, ist ein Teil von Gson ParameterizedTypeImpl
Aufgrund des Typs Löschen haben Klassen in Java keine Typparameter zur Laufzeit.
Die Type von Gson stellt keine tatsächlich geladenen Class.

Weitere Informationen finden Sie unter Get Type of ParameterizedType from generic.

Verwandte Themen