Pregunta ¿Cuál es la diferencia entre el nombre canónico, el nombre simple y el nombre de clase en Java Class?


En Java, ¿cuál es la diferencia entre estos:

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

He comprobado el Javadoc varias veces y, sin embargo, esto nunca lo explica bien. También hice una prueba y eso no refleja ningún significado real detrás de la forma en que se llaman estos métodos.


732
2018-03-04 13:45


origen


Respuestas:


Si no está seguro de algo, intente primero escribir una prueba.

Hice esto:

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

Huellas dactilares:

En t
En t
En t

java.lang.String
java.lang.String
Cuerda

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

ClassnameTest $ 1
nulo

Hay una línea vacía en el último bloque donde getSimpleName devuelve una cadena vacía

El resultado al mirar esto es:

  • el nombre es el nombre que utilizarías para cargar dinámicamente la clase con, por ejemplo, una llamada a Class.forName con el predeterminado ClassLoader.
  • el nombre Canonico es el nombre que se usaría en una declaración de importación e identifica de forma única la clase. Puede ser útil durante toString o operaciones de registro.
  • el nombre simple vagamente identifica la clase, de nuevo podría ser útil durante toString o las operaciones de registro, pero no se garantiza que sea único.

884
2018-03-04 14:07



Agregar matrices:

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

Agrega a la respuesta de Nick Holt:

[I
int[]
int[]

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

67
2018-05-08 15:06



Añadiendo clases locales, lambdas y toString() método para completar las dos respuestas anteriores. Además, agrego matrices de lambdas y matrices de clases anónimas (que sin embargo no tienen ningún sentido en la práctica):

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 {}

Este es el resultado completo:

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;

Entonces, aquí están las reglas. Primero, comencemos con tipos primitivos y void:

  1. Si el objeto de clase representa un tipo primitivo o void, los cuatro métodos simplemente devuelven su nombre.

Ahora las reglas para el getName() método:

  1. Cada clase o interfaz no lambda y no array (es decir, de nivel superior, anidado, interno, local y anónimo) tiene un nombre (que es devuelto por getName()) ese es el nombre del paquete seguido de un punto (si hay un paquete), seguido por el nombre de su archivo de clase generado por el compilador (sin el sufijo) .class) Si no hay paquete, es simplemente el nombre del archivo de clase. Si la clase es una clase interna, anidada, local o anónima, el compilador debe generar al menos una $ en su nombre de archivo de clase. Tenga en cuenta que para las clases anónimas, el nombre de clase terminaría con un signo de dólar seguido de un número.
  2. Los nombres de las clases de Lambda son generalmente impredecibles, y de todos modos no deberías preocuparte por ellos. Exactamente, su nombre es el nombre de la clase adjunta, seguido de $$Lambda$, seguido de un número, seguido de una barra, seguida de otro número.
  3. El descriptor de clase de los primitivos es Z para boolean, B para byte, S para short, C para char, I para int, J para long, F para float y D para double. Para las clases e interfaces que no son de matriz, el descriptor de clase es L seguido de lo que está dado por getName() seguido por ;. Para las clases de matriz, el descriptor de clase es [ seguido por el descriptor de clase del tipo de componente (que puede ser en sí mismo otra clase de matriz).
  4. Para las clases de matriz, el getName() método devuelve su descriptor de clase. Esta regla parece fallar solo para clases de array cuyo tipo de componente es un lambda (que posiblemente sea un error), pero con suerte esto no debería importar de todos modos porque no tiene sentido ni siquiera en la existencia de clases de matriz cuyo tipo de componente es un lambda.

Ahora el toString() método:

  1. Si la instancia de clase representa una interfaz (o una anotación, que es un tipo especial de interfaz), la toString() devoluciones "interface " + getName(). Si es un primitivo, regresa simplemente getName(). Si se trata de otra cosa (un tipo de clase, incluso si es bastante raro), regresa "class " + getName().

los getCanonicalName() método:

  1. Para clases e interfaces de nivel superior, el getCanonicalName() método devuelve exactamente lo que getName() método devuelve
  2. los getCanonicalName() método devuelve null para clases locales o anónimas y para clases de arreglos de esas.
  3. Para clases e interfaces internas y anidadas, getCanonicalName() método devuelve lo que getName() método reemplazaría los signos de dólar introducidos por el compilador por puntos.
  4. Para las clases de matriz, el getCanonicalName() método devuelve null si el nombre canónico del tipo de componente es null. De lo contrario, devuelve el nombre canónico del tipo de componente seguido por [].

los getSimpleName() método:

  1. Para las clases internas, locales y anidadas de nivel superior, getSimpleName() devuelve el nombre de la clase como está escrito en el archivo fuente.
  2. Para las clases anónimas getSimpleName() devuelve un vacío String.
  3. Para las clases de lambda getSimpleName() simplemente devuelve lo que getName() regresaría sin el nombre del paquete. Esto no tiene mucho sentido y parece un error para mí, pero no tiene sentido llamar getSimpleName() en una clase lambda para empezar.
  4. Para clases de matriz, getSimpleName() método devuelve el nombre simple de la clase de componente seguido por []. Esto tiene el extraño / extraño efecto secundario que tienen las clases de matriz cuyo tipo de componente es una clase anónima [] como sus nombres simples.

60
2018-04-08 23:33



También me ha confundido la amplia gama de diferentes esquemas de nombres, y estaba a punto de preguntar y responder mi propia pregunta sobre esto cuando encontré esta pregunta aquí. Creo que mis hallazgos encajan lo suficientemente bien y complementan lo que ya está aquí. Mi enfoque es buscar documentación en los diversos términos, y agregando algunos términos más relacionados que podrían surgir en otros lugares.

Considere el siguiente ejemplo:

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • los nombre simple de D es D. Esa es solo la parte que escribiste al declarar la clase. Clases anónimas no tener un nombre simple Class.getSimpleName() devuelve este nombre o la cadena vacía. Es posible que el nombre simple contenga un $ si lo escribes así, ya que $ es una parte válida de un identificador.

  • De acuerdo a la sección 6.7 de JLS, ambos a.b.C.D y a.b.C.D.D.D sería nombres completamente calificados, pero sólo a.b.C.D sería el nombre Canonico de D. Entonces, cada nombre canónico es un nombre completamente calificado, pero no siempre es cierto. Class.getCanonicalName() devolverá el nombre canónico o null.

  • Class.getName() está documentado para devolver el nombre binariocomo se especifica en JLS sección 13.1. En este caso, regresa a.b.C$D para D y [La.b.C$D; para D[].

  • Esta respuesta demuestra que es posible que dos clases cargadas por el mismo cargador de clases tengan el mismo nombre Canonico pero distinto nombres binarios. Ninguno de los dos nombres es suficiente para deducir el otro: si tiene el nombre canónico, no sabe qué partes del nombre son paquetes y cuáles contienen clases. Si tiene el nombre binario, no sabe qué $ se introdujeron como separadores y formaban parte de un nombre simple.

  • Clases anónimas y clases locales No tienen nombres completamente calificados pero aún tienes un nombre binario. Lo mismo vale para las clases anidadas dentro de dichas clases. Cada la clase tiene un nombre binario

  • Corriendo javap -v -private en a/b/C.class muestra que bytecode se refiere al tipo de d como La/b/C$D; y el de la matriz ds como [La/b/C$D;. Estos se llaman descriptores, y están especificados en JVMS sección 4.3.

  • El nombre de la clase a/b/C$D utilizado en ambos descriptores es lo que obtienes al reemplazar . por / en el nombre binario La especificación de JVM aparentemente llama a esto forma interna del nombre binario. JVMS sección 4.2.1 lo describe y dice que la diferencia con el nombre binario fue por razones históricas.

  • los nombre del archivo de una clase en uno de los cargadores de clase típicos basados ​​en nombre de archivo es lo que obtienes si interpretas el / en la forma interna del nombre binario como un separador de directorio, y anexar la extensión de nombre de archivo .class lo. Se resuelve en relación con la ruta de clase utilizada por el cargador de clase en cuestión.


6
2018-05-02 09:15



este es el mejor documento que encontré describiendo 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[]

6
2018-06-02 23:12



Es interesante notar que getCanonicalName() y getSimpleName() puede levantar InternalError cuando el nombre de la clase está mal formado. Esto sucede para algunos lenguajes JVM que no son Java, por ejemplo, Scala.

Considere lo siguiente (Scala 2.11 en 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

Esto puede ser un problema para entornos de idiomas mixtos o entornos que cargan de forma dinámica bytecode, por ejemplo, servidores de aplicaciones y otro software de plataforma.


2
2017-08-23 01:27



    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
2017-10-09 14:15