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

668

В Java: В чем разница между:

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

?

UPDATE

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

Итак, пожалуйста, вместо того, чтобы обвинять меня в том, что вы задаете этот вопрос и направляете меня на javadoc, постарайтесь приложить некоторые усилия, чтобы объяснить реальный смысл, стоящий за ними. Спасибо за вашу поддержку.

  • 190
    Я думаю, что это разумный вопрос. Javadoc не очень хорошо объясняет разницу между ними.
Показать ещё 4 комментария
Теги:

7 ответов

802
Лучший ответ

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

Я сделал это:

//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$1
null

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

Результат:

  • имя - это имя, которое вы бы использовали для динамической загрузки класса, например, с вызовом Class.forName с по умолчанию ClassLoader.
  • каноническое имя - это имя, которое будет использоваться в операторе импорта и однозначно идентифицирует класс. Может быть полезно во время toString или ведения журнала.
  • простое имя слабо идентифицирует класс, снова может быть полезно во время toString или протоколирования, но не гарантировано быть уникальным.
  • 5
    Какие дополнительные, как вы думаете, нужны?
  • 272
    Я пришел сюда в поисках кого-то, кто написал эту программу, чтобы мне не пришлось. Спасибо, Ник!
Показать ещё 14 комментариев
54

Добавление массивов:

    //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());

    System.out.println();

    {
    //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[]
  • 25
    Я не был бы намного лучше предложить изменить ответ выше.
53

Добавление локальных классов, lambdas и метода 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.TestClassNames$1LocalClass
getCanonicalName(): null
getSimpleName(): LocalClass
toString(): class com.example.TestClassNames$1LocalClass

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

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

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

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

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

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

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

Теперь правила для метода getName():

  1. Каждый класс или интерфейс, отличные от lambda и non-array (т.е. верхний уровень, вложенный, внутренний, локальный и анонимный) имеет имя (которое возвращается getName()), которое является именем пакета, за которым следует точка (если есть пакет), за которым следует имя его класса файла, сгенерированного компилятором (без суффикса .class). Если пакет отсутствует, это просто имя класса файла. Если класс является внутренним, вложенным, локальным или анонимным классом, компилятор должен сгенерировать по меньшей мере один $ в его имени класса. Обратите внимание, что для анонимных классов имя класса заканчивается значком доллара, за которым следует число.
  2. Имена классов Lambda обычно непредсказуемы, и вам все равно их не нужно. Точно, их имя - это имя окружающего класса, за которым следует $$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().

Метод getCanonicalName():

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

Метод getSimpleName():

  1. Для верхних, вложенных, внутренних и локальных классов getSimpleName() возвращает имя класса, как указано в исходном файле.
  2. Для анонимных классов getSimpleName() возвращает пустой String.
  3. Для лямбда-классов getSimpleName() просто возвращает то, что вернет getName() без имени пакета. Это не имеет большого смысла и выглядит как ошибка для меня, но нет смысла призывать getSimpleName() к классу лямбда для начала.
  4. Для классов массивов метод getSimpleName() возвращает простое имя класса компонента, за которым следует []. Это имеет смешной/странный побочный эффект, что классы массивов, тип компонента которых является анонимным классом, имеют просто [] как их простые имена.
  • 1
    … replacing the dollar-signs by dots : заменяются только знаки доллара, которые были введены в качестве разделителей. Вы можете иметь доллары как часть простого имени, и они останутся на месте.
  • 0
    о нет! Как часть имени класса! Я разрабатываю преобразователь класса, и я подумал, что «/» будет безопасным разделителем между классом и именем пакета: /
5

это лучший документ, который я нашел, описывая 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$1
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[]
4

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

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

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • простое имя 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 на a/b/C.class показывает, что байт-код относится к типу D как La/b/C$D;, а к массиву ds - как [La/b/C$D;. Они называются дескрипторами, и они указаны в JVMS раздел 4.3.

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

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

  • 0
    Это должен быть принятый ответ, так как это единственный ответ, который ссылается на JLS и использует правильные термины.
1

Интересно отметить, что getCanonicalName() и getSimpleName() могут поднять InternalError, когда имя класса искажено. Это происходит для некоторых языков Java, отличных от 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

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

1
    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
  • 1
    Первые две строки внутри метода могут быть сокращены до Class<StringBuffer> clazz = StringBuffer.class

Ещё вопросы

Сообщество Overcoder
Наверх
Меню