如何获取java类的二进制名称,如果一个只有完全限定的名称?

2022-09-03 14:40:49

反射类和方法以及类加载器等需要所谓的类的“二进制”名称来使用。

问题是,如果一个人只有完全限定的名称,即在源代码中使用的名称,那么如何获得二进制名称。

例如:

package frege;
public static class RT {
    ....
    public static class X { .... }
}

类的完全限定名将是 。然而,要获得类对象,需要编写:frege.RT.X

Class.forName("frege.RT$X")

而不是

Class.forName("frege.RT.X")    // fails with ClassNotFoundException

因为碰巧是 的内部类。Xfrege.RT

一个可能但笨拙的解决方案是从后面一个接一个地替换,直到不再扔掉或者没有更多可以替换的东西。.$Class.forName()ClassNotFoundException.

有没有更好/知名/标准的解决方案?我在API文档中查找,但没有找到任何有用的东西。ClassCLassLoaderjava.lang.reflect


答案 1

现在听起来好像你想从规范名称中获取完全限定的名称 (FQN)。由于这与从简单名称工作不同,我将添加第二个答案。

如果会导致规范名称冲突,则 Sun javac 命令将不会编译类。但是,通过单独编译,您仍然可以获得两个具有相同规范名称的不同类。

例如:

文件 src1\com\stack\Test.java

package com.stack;

public class Test {
    public static class Example {
        public static class Cow {
            public static class Hoof {
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Class<?> cl1 = Class.forName("com.stack.Test$Example$Cow$Hoof");
        Class<?> cl2 = Class.forName("com.stack.Test.Example.Cow.Hoof");
        System.out.println(cl1.getName());
        System.out.println(cl1.getSimpleName());
        System.out.println(cl1.getCanonicalName());
        System.out.println();
        System.out.println(cl2.getName());
        System.out.println(cl2.getSimpleName());
        System.out.println(cl2.getCanonicalName());
    }
}

文件 src2\com\stack\Test\Example\Cow\Hoof.java

package com.stack.Test.Example.Cow;

public class Hoof { }

然后编译并执行:

set CLASSPATH=
mkdir bin1 bin2
javac -d bin1 -sourcepath src1 src1\com\stack\Test.java
javac -d bin2 -sourcepath src2 src2\com\stack\Test\Example\Cow\Hoof.java

set CLASSPATH=bin1;bin2
java com.stack.Test

生成输出:

com.stack.Test$Example$Cow$Hoof
Hoof
com.stack.Test.Example.Cow.Hoof

com.stack.Test.Example.Cow.Hoof
Hoof
com.stack.Test.Example.Cow.Hoof

因此,两个类具有相同的规范名称,但 FQN 不同。即使两个类具有相同的 FQN 和相同的规范名称,如果它们通过不同的类装入器装入,它们仍然可能不同。

为了解决您的问题,我看到了您可以采取的几种前进方式。

首先,您可以指定匹配嵌套量最少的类,从而匹配 FQN 中 “$” 最少的类。更新事实证明,Sun javac与此完全相反,并且匹配具有最多嵌套的类。

其次,您可以测试所有可能的 FQN,如果有多个 FQN,则引发异常。

第三,接受唯一的唯一映射是使用 FQN,然后仅在指定的类装入器中,并适当地重新处理应用程序。我发现使用线程上下文类装入器作为缺省类装入器很方便。


答案 2

一个简单的名称会省略很多信息,并且可能有许多类具有相同的简单名称。这可能会使这成为不可能。例如:

package stack;

/**
 * 
 * @author Simon Greatrix
 */
public class TestLocal {

    public Object getObject1() {
        class Thing {
            public String toString() { 
                return "I am a Thing";
            }
        }
        return new Thing();
    }

    public Object getObject2() {
        class Thing {
            public String toString() { 
                return "I am another Thing";
            }
        }
        return new Thing();
    }

    public Object getObject3() {
        class Thing {
            public String toString() { 
                return "I am a rather different Thing";
            }
        }
        return new Thing();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        TestLocal test = new TestLocal();
        Object[] objects = new Object[] {
                test.getObject1(),                
                test.getObject2(),                
                test.getObject3()                
        };

        for(Object o : objects) {
            System.out.println("Object      : "+o);
            System.out.println("Simple Name : "+o.getClass().getSimpleName());
            System.out.println("Name        : "+o.getClass().getName());
        }
    }
}

这将生成输出:

Object      : I am a Thing
Simple Name : Thing
Name        : stack.TestLocal$1Thing
Object      : I am another Thing
Simple Name : Thing
Name        : stack.TestLocal$2Thing
Object      : I am a rather different Thing
Simple Name : Thing
Name        : stack.TestLocal$3Thing

如您所见,所有三个本地类都具有相同的简单名称。