java - 获取泛型类型的泛型参数

标签 java generics reflection nested-generics

对于一些背景,我正在为我正在开发的编程语言(即 JVM 语言)开发一些框架内容,并使用 Java 类测试一些框架,因此出现了下面所有奇怪的包装器。

<小时/>

所以,我的问题是,如何获取类型参数边界的类型变量?目前我有以下内容:

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

但是当遇到如下情况时,会失败并出现 StackOverflowException:

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

因为它只是不断地尝试获取 A 的类型参数。我一直无法找到获取类型变量的类型变量的方法...我尝试过使用 getGenericDeclaration,但它似乎没有返回我需要的内容。非常感谢任何帮助。

最佳答案

@csharpfolk 建议对已解析的内容进行统计并加以利用,这是正确的。下面是一个编译和可运行的示例,它演示了您的问题在实践中的情况。

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

输出:

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

您可以通过直接打印类型而不是泛型来清理打印,使其看起来不那么冗余。但这显示了该解决方案的所有显着特征。

关于java - 获取泛型类型的泛型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38162588/

相关文章:

java - 速度模板元数据

java - 如何使用 Tapestry 组件绘制钻石 t :loop

swift - 可以制作通用枚举 UIPickerView

c# - 使用activationCustomData参数创建类实例

java - Spring事务管理: using @Transactional vs using AOP (<aop:advisor)

java - 在 junit 5 中测试类中的 stub 私有(private)方法,PowerMock 不适用于 JUnit5

java - 如何在java中动态扩展一个类?

generics - Rust:对通用参数的引用不满足特征绑定(bind)

java - 有没有办法在java中模拟属性选择器?

java - 用于测试 Java 类结构的库或框架