В чем разница между каноническим именем, простым именем и именем класса в классе Java?
в Java, в чем разница между этими:
Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();
Я проверил Javadoc несколько раз, и все же это никогда не объясняет его хорошо.
Я также провел тест, и это не отражало никакого реального смысла за тем, как эти методы называются.
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:
- если объект класса представляет примитивный тип или
void, все четыре метода просто возвращает его имя.теперь правила
getName()способ:
- каждый класс или интерфейс без лямбды и без массива (т. е. верхний уровень, вложенный, внутренний, локальный и анонимный) имеет имя (которое возвращается
getName()) это имя пакета, за которым следует точка (если есть пакет), а затем имя его файла класса, созданного компилятором (без суффикса.class). Если пакета нет, то это просто имя класса-файла. Если класс является внутренним, вложенным, локальным или анонимным классом, компилятор должен генерировать хотя бы один$в своем классе-файла. Обратите внимание, что для анонимных классов имя класса заканчивается знаком доллара, за которым следует число.- имена классов лямбда, как правило, непредсказуемы, и вы не должны заботиться о них в любом случае. Точно, их имя-это имя заключающего класса, за которым следует
$$Lambda$, за которым следует число, затем Слэш, а затем еще один число.- дескриптор класса примитивов
Zнаboolean,Bнаbyte,Sнаshort,Cнаchar,Iнаint,Jнаlong,FнаfloatиDнаdouble. Для классов и интерфейсов без массива дескриптором класса являетсяLдалее следует то, что даноgetName()следовал по;. Для классов массива дескриптором класса является[за которым следует дескриптор класса тип компонента (который сам может быть другим классом массива).- для классов массива
getName()метод возвращает дескриптор класса. Это правило, похоже, не работает только для классов массивов, тип компонента которых является лямбда (что, возможно, является ошибкой), но, надеюсь, это не имеет значения в любом случае, потому что нет смысла даже в существовании классов массивов, тип компонента которых является лямбда.и
toString()способ:
- если класс экземпляр представляет собой интерфейс (или аннотацию, которая является специальным типом интерфейса),
toString()возвращает"interface " + getName(). Если это примитив, он возвращает простоgetName(). Если это что-то другое (тип класса, даже если он довольно странный), он возвращает"class " + getName().The
getCanonicalName()способ:
- для классов и интерфейсов верхнего уровня,
getCanonicalName()метод возвращает только то, чтоgetName()возвращает метод.- The
getCanonicalName()способ возвращаетnullдля анонимных или локальных классов и для классов массивов из них.- для внутренних и вложенных классов и интерфейсов,
getCanonicalName()метод возвращает то, чтоgetName()метод будет заменять введенные компилятором знаки доллара точками.- для классов массива
getCanonicalName()возвращаетnullесли каноническое имя типа компонентаnull. В противном случае он возвращает каноническое имя типа компонента, за которым следует[].The
getSimpleName()способ:
- для верхнего уровня, вложенных, внутренних и локальных классов,
getSimpleName()возвращает имя класса, записанное в исходном файле.- для анонимных классов
getSimpleName()возвращает пустуюString.- для лямбда-классов
getSimpleName()просто возвращает то, чтоgetName()вернется без имени пакета. Это не имеет смысла и выглядит как баг для меня, но нет смысла звонюgetSimpleName()на лямбда-класс для начала.- для классов массива
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 -privateona/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 ()
// 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