哪个java.lang.Class方法为Class.forName()生成正确的输入?

时间:2016-07-29 20:46:49

标签: java reflection

我想写一些这样的代码:

Object o = ...;
String oTypeName = o.getClass().getName();

//on the other side of the wire:

Class<?> oClass = Class.forName(oTypeName);
Object oAgain = oClass.newInstance();

但是,从javadoc中我不清楚我应该使用哪种方法来初始化oTypeName,即哪种方法会产生Class.forName()的预期输入:

  • getCanonicalName():&#34;返回Java语言规范定义的基础类的规范名称。如果基础类没有规范名称(即,如果它是本地或匿名类或其组件类型没有规范名称的数组),则返回null。&#34;
  • getName():&#34;返回此Class对象表示的实体名称(类,接口,数组类,基本类型或void),作为String。如果此类对象表示不是数组类型的引用类型,则返回该类的二进制名称,如Java™语言规范所指定。&#34;
  • getTypeName():&#34;返回此类型名称的信息字符串。&#34;

很明显,我不想要其中任何一个:

  • getSimpleName():&#34;返回源代码中给出的基础类的简单名称。&#34;
  • toString():&#34;字符串表示是字符串&#34; class&#34;或者&#34;接口&#34;,后跟一个空格,然后是getName&#34;
  • 返回的格式的类的完全限定名称。

我不希望这适用于原始类型。如果它不能为数组工作,那没关系。我关注的主要问题是嵌套类和Foo.BarFoo$Bar

4 个答案:

答案 0 :(得分:11)

明确的答案是getName()。虽然有点隐藏,但这是在forName(className, initialize, loader)

的重载的Javadoc中指定的
  

给定类或接口的完全限定名称(采用getName 返回的相同格式),此方法尝试查找,加载和链接类或接口。

并且还指定调用forName(className)等效于使用默认值调用此重载:

  

调用此方法相当于:

Class.forName(className, true, currentLoader) 
     

其中currentLoader表示当前类的定义类加载器。

这是一个示例代码,显示它适用于嵌套类,本地类,匿名类,基元或对象数组。它不适用于原语,因为Class.forName不处理原始类。

public class Main {
    public static void main(String... args) throws ClassNotFoundException {
        class LocalClass {}

        System.out.println(Class.forName(name(StaticNestedClass.class))); //static nested class
        System.out.println(Class.forName(name(InnerClass.class))); // inner class
        System.out.println(Class.forName(name(Integer[].class))); // object array
        System.out.println(Class.forName(name(int[].class))); // primitive array
        System.out.println(Class.forName(name(List.class))); // interface
        System.out.println(Class.forName(name(LocalClass.class))); // local class
        System.out.println(Class.forName(name(new Object(){}.getClass()))); // anonymous class
    }

    private static String name(Class<?> clazz) {
        return clazz.getName();
    }

    public static class StaticNestedClass {}
    public class InnerClass {}
}

答案 1 :(得分:2)

看起来getName()getTypeName()都有效,至少在简单的情况下如下:

public final class ForNameTest{

    public static void main(String[] args) throws Exception{
        Object o = new Foo();
        System.out.println("class is: " + o.getClass());

        for(String getterMethodName : Arrays.asList("getName", "getTypeName", "getCanonicalName")){
            Method m = Class.class.getMethod(getterMethodName);
            String oTypeName = m.invoke(o.getClass()).toString();
            System.out.println(getterMethodName + " yields " + oTypeName);
            try{
                Class<?> oType = Class.forName(oTypeName);
                Object oAgain = oType.newInstance();
                System.out.println(" ... and it works: " + oAgain);
            } catch (Exception e){
                System.err.println(" ... and it fails: " + e);
            }
        }
    }

    public static class Foo{}
}

产生的输出是:

class is: class ForNameTest$Foo
getName yields ForNameTest$Foo
 ... and it works: ForNameTest$Foo@4554617c
getTypeName yields ForNameTest$Foo
 ... and it works: ForNameTest$Foo@74a14482
getCanonicalName yields ForNameTest.Foo
 ... and it fails: java.lang.ClassNotFoundException: ForNameTest.Foo

答案 2 :(得分:1)

我总是使用getCanonicalName()内部对象(如你的Foo $ Bar如果静态公共vs内联实现)也可以构造。

此外,你可以使它与原语一起工作..&#39; int.class&#39;例如确实存在。但是,您可能必须检查基元类,并使Object实例(Integer vs int)然后像intValue()一样调用访问器。因此,我使用了很多对象实例和原始对象,但我认为这只是我的偏好。

答案 3 :(得分:-1)

Object oAgain = oClass.newInstance();

[编辑] 无问题哪个方法(getName(),getCanonicalName()等等)不能使用newInstance()方法为非静态内部类创建对象

如果使用newInstance()创建对象,则底层类必须包含no arg构造函数。即使我们显式插入一个无参数构造函数,编译器也会将它转换为带有一些参数的构造函数(仅在非静态内部类的情况下)

[结束编辑]

以下是我找到的简短代码的链接。它演示了上面的解释。

http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor