java - Class.forName 等效于从 String 创建 ParameterizedType

标签 java generics

调用 java.lang.reflect.Type.toString() 提供了非常好的通用类型表示:

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

我需要的是 Type.toString() 方法的逆向方法,即可以从给定的字符串表示形式创建 Type 的方法:

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

可以通过特殊类型的以下测试:

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

我找不到解决此问题的库或 SO 问题。

最佳答案

好吧......你可以自己做,例如antlr4使用以下语法

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'|'$'|'.'|'_')*
    ;

ClassBuilder 的样子

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

最后解析字符串

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

您可以查看工作示例 here .

注意

CLASS 词法分析器规则可能有点不准确。此外,此解析器仅适用于非原始类和参数化类型,但您当然可以扩展它以支持通配符类型。

关于java - Class.forName 等效于从 String 创建 ParameterizedType,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39401083/

相关文章:

c# - 为什么要调用父级的构造函数?

java - 我认为java中只有一种类加载器就足够了

java - AWS 无服务器请求 - JSON body/httpMethod 始终为 null

java - 无法识别 java.lang.ClassNotFoundException BaseDexClassLoader 的来源

java - 通过反射检查 Class<?> 是否是 other 的实例

java - Java泛型有没有办法模仿OR

java - 从泛型类派生类型

java - CXF 在单元测试中因 IncompatibleClassChangeError 异常而失败

java - 通过反射在 Java 中调用 getter : What's the fastest way to repeatedly call it (performance and scalability wise)?

Java 泛型比较器单例