В чем разница между каноническим именем, простым именем и именем класса в классе Java?



в Java, в чем разница между этими:



Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();


Я проверил Javadoc несколько раз, и все же это никогда не объясняет его хорошо.
Я также провел тест, и это не отражало никакого реального смысла за тем, как эти методы называются.

476   7  

7 ответов:

если вы не уверены в чем-то, попробуйте сначала написать тест.

Я сделал так:

//primitive
System.out.println(int.class.getName());
System.out.println(int.class.getCanonicalName());
System.out.println(int.class.getSimpleName());

System.out.println();

//class
System.out.println(String.class.getName());
System.out.println(String.class.getCanonicalName());
System.out.println(String.class.getSimpleName());

System.out.println();

//inner class
System.out.println(HashMap.SimpleEntry.class.getName());
System.out.println(HashMap.SimpleEntry.class.getCanonicalName());
System.out.println(HashMap.SimpleEntry.class.getSimpleName());        

System.out.println();

//anonymous inner class
System.out.println(new Serializable(){}.getClass().getName());
System.out.println(new Serializable(){}.getClass().getCanonicalName());
System.out.println(new Serializable(){}.getClass().getSimpleName());

принты:

int
int
int

java.lang.String
java.lang.String
String

java.util.AbstractMap$SimpleEntry
java.util.AbstractMap.SimpleEntry
SimpleEntry

ClassnameTest
null

есть пустая строка в последнем блоке, где getSimpleName возвращает пустую строку.

в результате глядя на это:

  • the имя - это имя, которое вы бы использовали для динамической загрузки класса, например, вызов Class.forName по умолчанию ClassLoader.
  • в каноническое имя - это имя, которое будет использоваться в инструкции import и однозначно идентифицирует класс. Может быть полезно во время toString или ведение журнала операций.
  • the простое имя слабо идентифицирует класс, снова может быть полезно во время toString или ведение журнала операций, но не гарантируется быть уникальным.

В дополнение к наблюдениям Ника Холта, я провел несколько случаев для Array тип данных:

//primitive Array
int demo[] = new int[5];
Class<? extends int[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());       

System.out.println();


//Object Array
Integer demo[] = new Integer[5]; 
Class<? extends Integer[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());

выше фрагмент кода печатает:

[I
int[]
int[]

[Ljava.lang.Integer;
java.lang.Integer[]
Integer[]

добавление локальных классов, лямбд и toString() метод для завершения двух предыдущих ответов. Кроме того, я добавляю массивы лямбд и массивы анонимных классов (которые на практике не имеют никакого смысла):

package com.example;

public final class TestClassNames {
    private static void showClass(Class<?> c) {
        System.out.println("getName(): " + c.getName());
        System.out.println("getCanonicalName(): " + c.getCanonicalName());
        System.out.println("getSimpleName(): " + c.getSimpleName());
        System.out.println("toString(): " + c.toString());
        System.out.println();
    }

    private static void x(Runnable r) {
        showClass(r.getClass());
        showClass(java.lang.reflect.Array.newInstance(r.getClass(), 1).getClass()); // Obtains an array class of a lambda base type.
    }

    public static class NestedClass {}

    public class InnerClass {}

    public static void main(String[] args) {
        class LocalClass {}
        showClass(void.class);
        showClass(int.class);
        showClass(String.class);
        showClass(Runnable.class);
        showClass(SomeEnum.class);
        showClass(SomeAnnotation.class);
        showClass(int[].class);
        showClass(String[].class);
        showClass(NestedClass.class);
        showClass(InnerClass.class);
        showClass(LocalClass.class);
        showClass(LocalClass[].class);
        Object anonymous = new java.io.Serializable() {};
        showClass(anonymous.getClass());
        showClass(java.lang.reflect.Array.newInstance(anonymous.getClass(), 1).getClass()); // Obtains an array class of an anonymous base type.
        x(() -> {});
    }
}

enum SomeEnum {
   BLUE, YELLOW, RED;
}

@interface SomeAnnotation {}

это полный вывод:

getName(): void
getCanonicalName(): void
getSimpleName(): void
toString(): void

getName(): int
getCanonicalName(): int
getSimpleName(): int
toString(): int

getName(): java.lang.String
getCanonicalName(): java.lang.String
getSimpleName(): String
toString(): class java.lang.String

getName(): java.lang.Runnable
getCanonicalName(): java.lang.Runnable
getSimpleName(): Runnable
toString(): interface java.lang.Runnable

getName(): com.example.SomeEnum
getCanonicalName(): com.example.SomeEnum
getSimpleName(): SomeEnum
toString(): class com.example.SomeEnum

getName(): com.example.SomeAnnotation
getCanonicalName(): com.example.SomeAnnotation
getSimpleName(): SomeAnnotation
toString(): interface com.example.SomeAnnotation

getName(): [I
getCanonicalName(): int[]
getSimpleName(): int[]
toString(): class [I

getName(): [Ljava.lang.String;
getCanonicalName(): java.lang.String[]
getSimpleName(): String[]
toString(): class [Ljava.lang.String;

getName(): com.example.TestClassNames$NestedClass
getCanonicalName(): com.example.TestClassNames.NestedClass
getSimpleName(): NestedClass
toString(): class com.example.TestClassNames$NestedClass

getName(): com.example.TestClassNames$InnerClass
getCanonicalName(): com.example.TestClassNames.InnerClass
getSimpleName(): InnerClass
toString(): class com.example.TestClassNames$InnerClass

getName(): com.example.TestClassNamesLocalClass
getCanonicalName(): null
getSimpleName(): LocalClass
toString(): class com.example.TestClassNamesLocalClass

getName(): [Lcom.example.TestClassNamesLocalClass;
getCanonicalName(): null
getSimpleName(): LocalClass[]
toString(): class [Lcom.example.TestClassNamesLocalClass;

getName(): com.example.TestClassNames
getCanonicalName(): null
getSimpleName(): 
toString(): class com.example.TestClassNames

getName(): [Lcom.example.TestClassNames;
getCanonicalName(): null
getSimpleName(): []
toString(): class [Lcom.example.TestClassNames;

getName(): com.example.TestClassNames$$Lambda/1175962212
getCanonicalName(): com.example.TestClassNames$$Lambda/1175962212
getSimpleName(): TestClassNames$$Lambda/1175962212
toString(): class com.example.TestClassNames$$Lambda/1175962212

getName(): [Lcom.example.TestClassNames$$Lambda;
getCanonicalName(): com.example.TestClassNames$$Lambda/1175962212[]
getSimpleName(): TestClassNames$$Lambda/1175962212[]
toString(): class [Lcom.example.TestClassNames$$Lambda;

Итак, вот правила. Во-первых, давайте начнем с примитивных типов и void:

  1. если объект класса представляет примитивный тип или void, все четыре метода просто возвращает его имя.

теперь правила getName() способ:

  1. каждый класс или интерфейс без лямбды и без массива (т. е. верхний уровень, вложенный, внутренний, локальный и анонимный) имеет имя (которое возвращается getName()) это имя пакета, за которым следует точка (если есть пакет), а затем имя его файла класса, созданного компилятором (без суффикса .class). Если пакета нет, то это просто имя класса-файла. Если класс является внутренним, вложенным, локальным или анонимным классом, компилятор должен генерировать хотя бы один $ в своем классе-файла. Обратите внимание, что для анонимных классов имя класса заканчивается знаком доллара, за которым следует число.
  2. имена классов лямбда, как правило, непредсказуемы, и вы не должны заботиться о них в любом случае. Точно, их имя-это имя заключающего класса, за которым следует $$Lambda$, за которым следует число, затем Слэш, а затем еще один число.
  3. дескриптор класса примитивов Z на boolean,B на byte,S на short,C на char,I на int,J на long,F на float и D на double. Для классов и интерфейсов без массива дескриптором класса является L далее следует то, что дано getName() следовал по ;. Для классов массива дескриптором класса является [ за которым следует дескриптор класса тип компонента (который сам может быть другим классом массива).
  4. для классов массива getName() метод возвращает дескриптор класса. Это правило, похоже, не работает только для классов массивов, тип компонента которых является лямбда (что, возможно, является ошибкой), но, надеюсь, это не имеет значения в любом случае, потому что нет смысла даже в существовании классов массивов, тип компонента которых является лямбда.

и toString() способ:

  1. если класс экземпляр представляет собой интерфейс (или аннотацию, которая является специальным типом интерфейса),toString() возвращает "interface " + getName(). Если это примитив, он возвращает просто getName(). Если это что-то другое (тип класса, даже если он довольно странный), он возвращает "class " + getName().

The getCanonicalName() способ:

  1. для классов и интерфейсов верхнего уровня,getCanonicalName() метод возвращает только то, что getName() возвращает метод.
  2. The getCanonicalName() способ возвращает null для анонимных или локальных классов и для классов массивов из них.
  3. для внутренних и вложенных классов и интерфейсов,getCanonicalName() метод возвращает то, что getName() метод будет заменять введенные компилятором знаки доллара точками.
  4. для классов массива getCanonicalName() возвращает null если каноническое имя типа компонента null. В противном случае он возвращает каноническое имя типа компонента, за которым следует [].

The getSimpleName() способ:

  1. для верхнего уровня, вложенных, внутренних и локальных классов,getSimpleName() возвращает имя класса, записанное в исходном файле.
  2. для анонимных классов getSimpleName() возвращает пустую String.
  3. для лямбда-классов getSimpleName() просто возвращает то, что getName() вернется без имени пакета. Это не имеет смысла и выглядит как баг для меня, но нет смысла звоню getSimpleName() на лямбда-класс для начала.
  4. для классов массива getSimpleName() метод возвращает простое имя класса компонента, за которым следует []. Это имеет забавный / странный побочный эффект, что классы массива, тип компонента которых является анонимным классом, имеют только [] как их простые имена.

я был смущен широким спектром различных схем именования, а также, и как раз собирался задать и ответить на мой собственный вопрос об этом, когда я нашел этот вопрос здесь. Я думаю, что мои выводы достаточно хорошо подходят и дополняют то, что уже здесь. Мой фокус ищет документация на различные сроки, и добавив еще несколько терминов, которые могут возникнуть в других местах.

рассмотрим следующий пример:

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • The простое имя на D и D. Это просто часть, которую вы написали при объявлении класса. анонимные классы не имеют простого имени. Class.getSimpleName() возвращает это имя или пустую строку. Это возможно для простого имени, чтобы содержать $ если вы пишете это так, так как $ является допустимой частью идентификатора.

  • по данным JLS раздел 6.7, и a.b.C.D и a.b.C.D.D.D будет полные имена, но только a.b.C.D будет каноническое имя на D. Таким образом, каждое каноническое имя является полным именем, но обратное не всегда верно. Class.getCanonicalName() возвращает каноническое имя или null.

  • Class.getName() документируется для возврата двоичное имя, как указано в раздел JLS 13.1. В этом случае он возвращает a.b.C$D на D и [La.b.C$D; на D[].

  • ответ демонстрирует, что два класса, загруженные одним и тем же загрузчиком классов, могут иметь один и тот же каноническое имя но отчетливый двоичные имена. Ни одно имя не является достаточным для надежного вывода другого: если у вас есть каноническое имя, вы не знаете, какие части имени являются пакетами, а какие содержат занятия. Если у вас есть двоичное имя, вы не знаете, какой $ были введены как разделители и которые были частью некоторого простого имени.

  • анонимные классы и локальные классы нет полные имена но все равно двоичное имя. То же самое относится и к классам, вложенным в такие классы. каждый класс имеет двоичное имя.

  • запуск javap -v -private on a/b/C.class показывает, что байт-код относится к типу d как La/b/C$D; и что из массива ds как [La/b/C$D;. Они называются дескрипторов, и они указаны в JVMS раздел 4.3.

  • имя класса a/b/C$D используется в обоих этих дескрипторов является то, что вы получаете, заменив . by / в двоичном имени. Спецификация JVM, по-видимому, называет это внутренняя форма двоично имя. JVMS раздел 4.2.1 описывает его и утверждает, что отличие от двоичного имени было по историческим причинам.

  • The имя файла класса в одном из типичных загрузчиков классов на основе имени файла-это то, что вы получаете, если интерпретируете / во внутренней форме двоичного имени в качестве разделителя каталогов и добавьте расширение имени файла .class к нему. Он разрешен относительно пути к классу, используемого загрузчик классов в вопросе.

Это лучший документ, который я нашел, описывающий getName(), getSimpleName (), getCanonicalName ()

https://javahowtodoit.wordpress.com/2014/09/09/java-lang-class-what-is-the-difference-between-class-getname-class-getcanonicalname-and-class-getsimplename/

// Primitive type
int.class.getName();          // -> int
int.class.getCanonicalName(); // -> int
int.class.getSimpleName();    // -> int

// Standard class
Integer.class.getName();          // -> java.lang.Integer
Integer.class.getCanonicalName(); // -> java.lang.Integer
Integer.class.getSimpleName();    // -> Integer

// Inner class
Map.Entry.class.getName();          // -> java.util.Map$Entry
Map.Entry.class.getCanonicalName(); // -> java.util.Map.Entry
Map.Entry.class.getSimpleName();    // -> Entry     

// Anonymous inner class
Class<?> anonymousInnerClass = new Cloneable() {}.getClass();
anonymousInnerClass.getName();          // -> somepackage.SomeClass
anonymousInnerClass.getCanonicalName(); // -> null
anonymousInnerClass.getSimpleName();    // -> // An empty string

// Array of primitives
Class<?> primitiveArrayClass = new int[0].getClass();
primitiveArrayClass.getName();          // -> [I
primitiveArrayClass.getCanonicalName(); // -> int[]
primitiveArrayClass.getSimpleName();    // -> int[]

// Array of objects
Class<?> objectArrayClass = new Integer[0].getClass();
objectArrayClass.getName();          // -> [Ljava.lang.Integer;
objectArrayClass.getCanonicalName(); // -> java.lang.Integer[]
objectArrayClass.getSimpleName();    // -> Integer[]

интересно отметить, что getCanonicalName() и getSimpleName() можно поднять InternalError когда имя класса искажено. Это происходит для некоторых языков JVM, отличных от Java, например, Scala.

рассмотрим следующее (Scala 2.11 на Java 8):

scala> case class C()
defined class C

scala> val c = C()
c: C = C()

scala> c.getClass.getSimpleName
java.lang.InternalError: Malformed class name
  at java.lang.Class.getSimpleName(Class.java:1330)
  ... 32 elided

scala> c.getClass.getCanonicalName
java.lang.InternalError: Malformed class name
  at java.lang.Class.getSimpleName(Class.java:1330)
  at java.lang.Class.getCanonicalName(Class.java:1399)
  ... 32 elided

scala> c.getClass.getName
res2: String = C

это может быть проблемой для смешанных языковых сред или сред, которые динамически загружают байт-код, например, серверы приложений и другое программное обеспечение платформы.

    public void printReflectionClassNames(){
    StringBuffer buffer = new StringBuffer();
    Class clazz= buffer.getClass();
    System.out.println("Reflection on String Buffer Class");
    System.out.println("Name: "+clazz.getName());
    System.out.println("Simple Name: "+clazz.getSimpleName());
    System.out.println("Canonical Name: "+clazz.getCanonicalName());
    System.out.println("Type Name: "+clazz.getTypeName());
}

outputs:
Reflection on String Buffer Class
Name: java.lang.StringBuffer
Simple Name: StringBuffer
Canonical Name: java.lang.StringBuffer
Type Name: java.lang.StringBuffer

Comments

    Ничего не найдено.