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

avatar
Mohamed Taher Alrefaie
4 марта 2013 в 13:45
277367
8
1082

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

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

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

Источник
Nick Holt
4 марта 2013 в 13:50
1

См. - docs.oracle.com/javase/6/docs/api/java/lang/Class.html или, может быть, просто напишите тест.

vbence
5 июня 2014 в 16:14
7

@GrahamBorland В документации javadoc говорится, что "как определено спецификацией языка Java" - так что вы можете найти это в этом документе. Просто потому, что это не интерактивная ссылка, люди могут приложить минимальные усилия и щелкнуть по первому результату поисковой системы.

pathikrit
19 ноября 2014 в 11:00
71

@vbence: Большинство людей предпочли бы довести дело до конца, чем искать в JLS такие тривиальные вещи. Значит, это первый результат Google :)

Stephan
2 октября 2017 в 15:59
0

См. Также: coderwall.com/p/lap9ww/…

Ответы (8)

avatar
Nick Holt
4 марта 2013 в 14:07
1236

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

Я сделал это:

class ClassNameTest {
    public static void main(final String... arguments) {
        printNamesForClass(
            int.class,
            "int.class (primitive)");
        printNamesForClass(
            String.class,
            "String.class (ordinary class)");
        printNamesForClass(
            java.util.HashMap.SimpleEntry.class,
            "java.util.HashMap.SimpleEntry.class (nested class)");
        printNamesForClass(
            new java.io.Serializable(){}.getClass(),
            "new java.io.Serializable(){}.getClass() (anonymous inner class)");
    }

    private static void printNamesForClass(final Class<?> clazz, final String label) {
        System.out.println(label + ":");
        System.out.println("    getName():          " + clazz.getName());
        System.out.println("    getCanonicalName(): " + clazz.getCanonicalName());
        System.out.println("    getSimpleName():    " + clazz.getSimpleName());
        System.out.println("    getTypeName():      " + clazz.getTypeName()); // added in Java 8
        System.out.println();
    }
}

Печать:

int.class (primitive):
    getName():          int
    getCanonicalName(): int
    getSimpleName():    int
    getTypeName():      int

String.class (ordinary class):
    getName():          java.lang.String
    getCanonicalName(): java.lang.String
    getSimpleName():    String
    getTypeName():      java.lang.String

java.util.HashMap.SimpleEntry.class (nested class):
    getName():          java.util.AbstractMap$SimpleEntry
    getCanonicalName(): java.util.AbstractMap.SimpleEntry
    getSimpleName():    SimpleEntry
    getTypeName():      java.util.AbstractMap$SimpleEntry

new java.io.Serializable(){}.getClass() (anonymous inner class):
    getName():          ClassNameTest$1
    getCanonicalName(): null
    getSimpleName():    
    getTypeName():      ClassNameTest$1

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

Результат, глядя на это:

  • имя - это имя, которое вы бы использовали для динамической загрузки класса, например, при вызове Class.forName со значением по умолчанию ClassLoader. В рамках определенного ClassLoader все классы имеют уникальные имена.
  • каноническое имя - это имя, которое будет использоваться в операторе импорта. Это может быть полезно во время toString или операций записи в журнал. Когда компилятор javac имеет полное представление о пути к классам, он обеспечивает уникальность канонических имен в нем, противореча полностью определенным именам классов и пакетов во время компиляции. Однако JVM должны принимать такие конфликты имен, и, следовательно, канонические имена не могут однозначно идентифицировать классы в пределах ClassLoader. (Оглядываясь назад, лучшим названием для этого получателя было бы getJavaName; но этот метод восходит к тому времени, когда JVM использовалась исключительно для запуска программ Java.)
  • простое имя слабо идентифицирует класс, опять же может быть полезно во время toString или операций записи в журнал, но его уникальность не гарантируется.
  • имя типа возвращает «информативную строку для имени этого типа», «Это похоже на toString: оно чисто информативное и не имеет значения контракта». (как написано sir4ur0n)

Также вы можете часто ссылаться на документацию по спецификации языка Java для этих типов технических деталей Java API:

Example 6.7-2. и Example 6.7-2. переходят к Fully Qualified Names и Fully Qualified Names v. Canonical Name соответственно

Nick Holt
4 марта 2013 в 17:55
5

Как вы думаете, что еще нужно?

Jayen
7 июля 2014 в 07:13
0

так канонические имена неоднозначны? например у вас мог бы быть пакет java.util.AbstractMap с не внутренним классом SimpleEntry и с тем же каноническим именем, что и выше?

Anupam Saini
23 июля 2014 в 10:37
1

@Jayen На практике я никогда не видел пакета с именем java.util.AbstractMap. Каждое значение после "." начинается со строчной буквы. Вышеупомянутый пакет всегда будет определяться как java.util.abstractMap. Между тем, имя класса всегда будет AbstractMap

Jayen
23 июля 2014 в 10:58
2

@AnupamSaini, да. Было бы безумием иметь такое имя пакета в реальном приложении.

corsiKa
10 августа 2014 в 03:56
3

ИТ было бы безумием, однако такое предположение позволило бы злоумышленнику сработать. Кто-то говорит: «Ну, мы знаем, что классы никогда не будут начинаться со строчных букв / пакеты никогда не будут начинаться с заглавных букв». Конечно, злоумышленник, имеющий доступ к вашему загрузчику классов, уже может творить ужасные вещи, так что это, вероятно, не совсем ужасное предположение.

Lajos Mészáros
11 февраля 2015 в 10:15
1

getCanonicalName () полезен, если вы хотите создать класс из строки, потому что Class.toString () добавляет строку «class» перед именем класса.

Pieter De Bie
19 мая 2015 в 13:11
1

Прежде чем вы сможете написать такой тест, вам уже нужно знать разницу между getName () и getCanonicalName ().

fool4jesus
10 сентября 2015 в 13:29
3

@PieterDeBie Как так? Все, что вам нужно знать, это имя метода, который вы хотите протестировать.

Pieter De Bie
11 сентября 2015 в 08:26
1

@ fool4jesus, извините, я действительно не помню мотивы моего комментария, но, похоже, вы правы.

Theodore Murdock
16 мая 2016 в 15:59
24

Java 8 также добавила getTypeName () ... хотите обновить для этого?

X-HuMan
7 июля 2016 в 15:21
0

Согласитесь с @TheodoreMurdock, было бы неплохо, если бы вы обновили это, потому что ваш ответ имеет много голосов, и люди очень легко заметят изменение.

Sir4ur0n
11 августа 2016 в 08:24
0

Хотя это может быть круто, я думаю, что javadoc getTypeName() довольно ясен: Return an informative string for the name of this type.. Это похоже на toString (): он чисто информативный и не имеет контрактной стоимости.

Adowrath
8 апреля 2017 в 11:55
2

Глядя на реализацию в jdk8_102 (стандартный Oracle JDK), кажется, что это просто getName() с той дополнительной разницей, что массивы представлены не как [[[Ljava.lang.Object;, а как java.lang.Object[][][].

Per Lundberg
5 марта 2019 в 10:50
1

@TheodoreMurdock Добавлен вывод getTypeName(), как было предложено.

BPS
7 ноября 2019 в 14:26
0

Как говорит @Adowrath, массивы - это очень особенный и особенно неприятный случай. Другой ответ касается этого.

avatar
Abdul Alim Shakir
7 октября 2019 в 03:54
2

getName () - возвращает имя объекта (класса, интерфейса, класса массива, примитивного типа или void), представленного этим объектом Class, в виде String.

getCanonicalName () - возвращает каноническое имя базового класса, как определено в Спецификации языка Java.

getSimpleName () - возвращает простое имя базового класса, то есть имя, данное ему в исходном коде.

package com.practice;

public class ClassName {
public static void main(String[] args) {

  ClassName c = new ClassName();
  Class cls = c.getClass();

  // returns the canonical name of the underlying class if it exists
  System.out.println("Class = " + cls.getCanonicalName());    //Class = com.practice.ClassName
  System.out.println("Class = " + cls.getName());             //Class = com.practice.ClassName
  System.out.println("Class = " + cls.getSimpleName());       //Class = ClassName
  System.out.println("Class = " + Map.Entry.class.getName());             // -> Class = java.util.Map$Entry
  System.out.println("Class = " + Map.Entry.class.getCanonicalName());    // -> Class = java.util.Map.Entry
  System.out.println("Class = " + Map.Entry.class.getSimpleName());       // -> Class = Entry 
  }
}

Одно отличие состоит в том, что если вы используете анонимный класс , вы можете получить нулевое значение при попытке получить имя класса с помощью getCanonicalName()

Другой факт заключается в том, что метод getName() ведет себя иначе, чем метод getCanonicalName() для внутренних классов . getName() использует доллар в качестве разделителя между каноническим именем включающего класса и простым именем внутреннего класса.

Чтобы узнать больше о получении имени класса в Java.

avatar
Sim
23 августа 2017 в 01:27
5

Интересно отметить, что 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

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

avatar
Shirish Singh
9 октября 2016 в 14:15
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
ThePyroEagle
29 декабря 2016 в 16:08
1

Первые две строки внутри метода можно сократить до Class<StringBuffer> clazz = StringBuffer.class

avatar
Kiran
2 июня 2016 в 23:12
10

это лучший документ, который я нашел, описывающий 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[]
avatar
MvG
2 мая 2016 в 09:15
21

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

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

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
MEE
9 июля 2018 в 14:37
4

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

avatar
Victor Stafusa
8 апреля 2015 в 23:33
111

Добавление локальных классов, лямбда-выражений и метода 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:

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

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

  1. Каждый класс или интерфейс, не являющийся лямбда и не массивом (т. Е. Верхний уровень, вложенный, внутренний, локальный и анонимный), имеет имя (которое возвращается с помощью getName()), которое представляет собой имя пакета, за которым следует точка (если есть пакет), за которым следует имя его класс-файла, сгенерированного компилятором (без суффикса .class). Если пакета нет, это просто имя файла класса. Если класс является внутренним, вложенным, локальным или анонимным, компилятор должен сгенерировать по крайней мере один $ в своем имени файла класса. Обратите внимание, что для анонимных классов имя класса будет заканчиваться знаком доллара, за которым следует число.
  2. Имена классов лямбда, как правило, непредсказуемы, и вам все равно не нужно о них заботиться. Точно, их имя - это имя включающего класса, за которым следует $$Lambda$, за которым следует число, за которым следует косая черта, за которым следует другой номер.
  3. Дескриптор класса примитивов: Z для boolean, B для byte, S для short, C для short, C для <5330581153> 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() возвращает простое имя класса компонента, за которым следует []. Это имеет забавный / странный побочный эффект: классы массивов, тип компонента которых является анонимным, имеют только [] в качестве своих простых имен.
MvG
2 мая 2016 в 09:34
4

… replacing the dollar-signs by dots: заменяются только знаки доллара, которые были введены как разделители. Вы можете использовать доллары как часть простого имени, и они останутся на своем месте.

José Roberto Araújo Júnior
9 июня 2017 в 17:55
0

О, нет! Как часть имени класса! Я разрабатываю преобразователь классов, и я подумал, что '/' будет безопасным разделителем между классом и именем пакета: /

avatar
gerardw
8 мая 2014 в 15:06
83

В дополнение к наблюдениям Ника Холта я проверил несколько случаев для типа данных 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[]
LoKi
27 ноября 2015 в 13:07
29

Было бы гораздо лучше предложить правку к приведенному выше ответу.