Какой метод java.lang.Class генерирует правильный ввод для Class.forName()?

Я хотел бы написать код вроде этого:

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():" Возвращает каноническое имя базового класса, как определено спецификацией языка Java. Возвращает null, если базовый класс не имеют канонического имени (т.е. если это локальный или анонимный класс или массив, у которого тип компонента не имеет канонического имени).
  • getName():" Возвращает имя сущности (класс, интерфейс, класс массива, примитивный тип или void), представленный этим Объект класса в виде строки. Если этот объект класса представляет ссылочный тип, который не является типом массива, тогда возвращается двоичное имя класса, как указано в Спецификации языка Java ™.
  • getTypeName(): "Верните информативную строку для имени этого типа".

Довольно очевидно, что я не хочу ни одного из них:

  • getSimpleName(): "Возвращает простое имя базового класса, как указано в исходном коде".
  • toString(): "Строковое представление представляет собой строку" класс "или" интерфейс ", за которой следует пробел, а затем полное имя класса в формате, возвращаемом getName"

Я не ожидаю, что это будет работать для примитивных типов. Это нормально, если он не будет работать для массивов. Главное, что меня беспокоит, это вложенные классы и Foo.Bar vs. Foo$Bar.

Ответ 1

Определенный ответ getName(). Хотя бит скрыт, это указано в Javadoc перегрузки forName(className, initialize, loader):

Учитывая полное имя класса или интерфейса (в том же формате, который был возвращен 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 {}
}

Ответ 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: [email protected]
getTypeName yields ForNameTest$Foo
 ... and it works: [email protected]
getCanonicalName yields ForNameTest.Foo
 ... and it fails: java.lang.ClassNotFoundException: ForNameTest.Foo

Ответ 3

Я всегда использую getCanonicalName() Внутренние объекты (например, ваш Foo $Bar). Также можно построить статическую публичную и встроенную реализацию).

Также вы можете заставить его работать с примитивами. Например, "int.class" существует. Однако вам, вероятно, придется выполнить проверку классов примитивов и создать экземпляр Object (Integer vs int), а затем вызвать аксессуар, например, intValue(). Из-за этого я использую много экземпляров объектов против примитива, но это только мое предпочтение, я думаю.

Ответ 4

Object oAgain = oClass.newInstance();

[EDIT] No Matter, какой метод (getName(), getCanonicalName() и т.д.) Вы не можете использовать метод newInstance() для создания объекта для нестатического внутреннего класса

Если вы создаете объект с помощью newInstance(), то обязательно, чтобы базовый класс не содержал конструктор arg. Даже если мы явно вставим один конструктор без аргументов, компилятор преобразует его в конструктор с некоторыми аргументами (только в случае нестационарного внутреннего класса)

[END EDIT]

Ниже приведена ссылка на короткий код, который я нашел. Это демонстрирует объяснение выше.

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