В Java: В чем разница между:
Object o1= ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();
?
Я уже проверял Javadoc несколько раз, но это никогда не объясняет это хорошо. Я также проверил тест, и это не отражало никакого реального смысла в том, как называются эти методы.
Итак, пожалуйста, вместо того, чтобы обвинять меня в том, что вы задаете этот вопрос и направляете меня на javadoc, постарайтесь приложить некоторые усилия, чтобы объяснить реальный смысл, стоящий за ними. Спасибо за вашу поддержку.
Если вы что-то не уверены, попробуйте сначала написать тест.
Я сделал это:
//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
или протоколирования, но не гарантировано быть уникальным.Добавление массивов:
//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[]
Добавление локальных классов, 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()
:
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()
.Метод getCanonicalName()
:
getCanonicalName()
возвращает только то, что возвращает метод getName()
.getCanonicalName()
возвращает null
для анонимных или локальных классов и для классов массивов из них.getCanonicalName()
возвращает то, что метод getName()
заменит введенные компилятором знаки доллара точками.getCanonicalName()
возвращает null
, если каноническое имя типа компонента null
. В противном случае он возвращает каноническое имя типа компонента, за которым следует []
.Метод getSimpleName()
:
getSimpleName()
возвращает имя класса, как указано в исходном файле.getSimpleName()
возвращает пустой String
.getSimpleName()
просто возвращает то, что вернет getName()
без имени пакета. Это не имеет большого смысла и выглядит как ошибка для меня, но нет смысла призывать getSimpleName()
к классу лямбда для начала.getSimpleName()
возвращает простое имя класса компонента, за которым следует []
. Это имеет смешной/странный побочный эффект, что классы массивов, тип компонента которых является анонимным классом, имеют просто []
как их простые имена.… replacing the dollar-signs by dots
: заменяются только знаки доллара, которые были введены в качестве разделителей. Вы можете иметь доллары как часть простого имени, и они останутся на месте.
это лучший документ, который я нашел, описывая 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$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[]
Я был смущен широким диапазоном различных схем именования, и я собирался спросить и ответить на мой собственный вопрос, когда я нашел этот вопрос здесь. Я думаю, что мои результаты соответствуют этому достаточно хорошо и дополняют то, что уже здесь. В центре внимания находится документация по различным терминам и добавление некоторых связанных терминов, которые могут возникнуть в других местах.
Рассмотрим следующий пример:
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
. Он разрешен относительно пути класса, используемого рассматриваемым загрузчиком класса.
Интересно отметить, что 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
Это может быть проблемой для смешанных языковых сред или сред, которые динамически загружают байт-код, например, серверы приложений и другое программное обеспечение платформы.
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
Class<StringBuffer> clazz = StringBuffer.class