我如何知道一个 class 是否是另一个的超 class,因为我只有 classes 的完全限定名称作为字符串?

How do I know if one class is superclass of another, given that I only have the fully qualified names of the classes as strings?

我需要知道一个字符串表示的 class 是否超class(直接或传递)另一个 class 也表示为字符串。例如:

String class1 = "java.lang.Object";
String class2 = "java.lang.Exception";

assertTrue( isSuperclass( class1, class2 ) );

我试图获取第二个 class 的 IType 并检查它的超级class名称。我就是这样尝试的:

/**
 * Checks if class1 is directly or transitively superclass of class2.
 */
private boolean isSuperclass(final String class1, final String class2)
{
    try
    {
        String className = class2;
        while (className!=null)
        {
            final IType type = this.javaProject.findType(className);
            if (type == null)
            {
                break;
            }

            className = type.getSuperclassName();
            if (class1.equals(className))
            {
                return true;
            }
        }

        return false;
    }
    catch (final JavaModelException e)
    {
        throw new IllegalStateException(e);
    }
}

问题是 getSuperclassName return 只是 class 名称,而不是它的完全限定名称。出于这个原因,我不能在循环中再次调用 javaProject.findType 来检查 class2 的任何传递 superclass 是否等于 class1.

所以如果我调用:

isSuperclass( "java.lang.Throwable", "my.exceptions.Exception" )

它将 return 为假,因为

 javaProject.findType("my.exceptions.Exception").getSuperclassName() 

return只"ParentException",那么下次调用会是

 javaProject.findType("ParentException") 

其中 return 是空 IType,因为它不是 class 完全限定名称。这仅发生在正在分析的自己的应用程序中声明的 classes; for API classes getSuperclassName returns 完全限定名称。

我也考虑过通过 ITypeBindings 检查 isSuperclass,但我不知道如何从完全限定名称字符串或 IType 中获取 ITypeBinding。

关于如何检查 class 是否是另一个的超级class 的任何想法?

编辑:基于 Class.forName 的解决方案不适用于我的上下文。

如果你实际上可以加载有问题的类,你可以使用Class#isAssignableFrom:

Class c1 = Class.forName(class1);
Class c2 = Class.forName(class2);
if (c1.isAssignableFrom(c2)) {
    // c1 is a superclass or superinterface of c2
}

来自文档:

public boolean isAssignableFrom(Class<?> cls)

Determines if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter. It returns true if so; otherwise it returns false.

例如:

String class1 = "java.lang.Object";
String class2 = "java.util.HashMap";
Class c1 = Class.forName(class1);
Class c2 = Class.forName(class2);
System.out.println(c1.isAssignableFrom(c2)); // true

Live Example

您可以做的一个选择是使用 Class.forName() 获取 Class 对象,然后使用 obj.getSuperClass()obj.getCanonicalName() 检查继承。以下程序对此进行了演示:

    public static void main(String[] args) {
    try {
        Class str = Class.forName("java.lang.String");
        Class ex = Class.forName("java.lang.StringBuilder");
        Class superStr = str;
        Class superEx = ex;

        Boolean strIsSuperEx = false;
        Boolean exIsSuperStr = false;
        Boolean bothClassesExist = true;

        while((!strIsSuperEx && !exIsSuperStr) && bothClassesExist) {
            if (superStr != null) {
                superStr = superStr.getSuperclass();
            }
            if (superEx != null) {
                superEx = superEx.getSuperclass();
            }
            bothClassesExist = (superStr != null) && (superEx != null);
            if (superStr != null) {
                String canonicalName = superStr.getCanonicalName();
                String exName = ex.getCanonicalName();
                exIsSuperStr = exName.equals(canonicalName);
            }
            if (superEx != null) {
                String canonicalName = superEx.getCanonicalName();
                String strName = str.getCanonicalName();
                strIsSuperEx = strName.equals(canonicalName);
            }
        }

        if (strIsSuperEx) {
            System.out.println(str.getCanonicalName() + " is a superclass of " + ex.getCanonicalName());
        } else if (exIsSuperStr) {
            System.out.println(ex.getCanonicalName() + " is a superclass of " + str.getCanonicalName());
        } else {
            System.out.println("Neither class is a superclass of the other.");
        }

    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

基于 T.J 的回答。 Crowder,Brian 的评论和 this page 中的代码,我构建了这个答案:

public boolean isSuperclass(final String className1, final String className2)
{
    //build a URL Class Loader
    final String[] classPathEntries = JavaRuntime.computeDefaultRuntimeClassPath(iJavaProject);
    final List<URL> urlList = new ArrayList<URL>();
    for (int i = 0; i < classPathEntries.length; i++)
    {
        final String entry = classPathEntries[i];
        final IPath path = new Path(entry);
        final URL url = path.toFile().toURI().toURL();
        urlList.add(url);
    }
    final ClassLoader parentClassLoader = iJavaProject.getClass().getClassLoader();
    final URL[] urls = urlList.toArray(new URL[urlList.size()]);
    final URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader);

    // check if is superclass
    final Class<?> class1 = Class.forName(className1, true, classLoader);
    final Class<?> class2 = Class.forName(className2, true, classLoader);
    return class1.isAssignableFrom(class2);
}

由于您的问题中有 IType,我假设您使用的是 JDT。在这种情况下,您可以通过创建 ITypeHierarchie.

来查询 IType 的所有超类型(类 和接口)

唯一需要注意的是java.lang.Object不会被包含在ITypeHierarchie中(参见ITypeHierarchy.getAllSupertypesJavadoc),所以你需要检查一下单独为此。

if (class1.equals("java.lang.Object")
    return true;
ITypeHierarchy typeHierarchy = type.newTypeHierarchy(new NullProgressMonitor());
IType[] supertypes = typeHierarchy.getAllSupertypes(type);
boolean isSuperType = Arrays.asList(supertypes).stream().anyMatch(supertype -> {
    return supertype.getFullyQualifiedName().equals(class1);
});