Question Quelle est la différence entre un nom canonique, un nom simple et un nom de classe dans Java Class?


En Java, quelle est la différence entre ceux-ci:

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

J'ai vérifié le Javadoc plusieurs fois et pourtant cela ne l'explique jamais bien. J'ai aussi fait un test et cela ne reflétait pas la véritable signification derrière la façon dont ces méthodes sont appelées.


732
2018-03-04 13:45


origine


Réponses:


Si vous n'êtes pas sûr de quelque chose, essayez d'écrire un test en premier.

J'ai fait ça:

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

Impressions:

int
int
int

java.lang.String
java.lang.String
Chaîne

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

ClassnameTest $ 1
nul

Il y a une ligne vide dans le dernier bloc où getSimpleName retourne une chaîne vide.

Le résultat en regardant ceci est:

  • la prénom est le nom que vous utiliseriez pour charger dynamiquement la classe avec, par exemple, un appel à Class.forName avec la valeur par défaut ClassLoader.
  • la nom canonique est le nom qui serait utilisé dans une instruction import et identifie de manière unique la classe. Peut être utile pendant toString ou des opérations de journalisation.
  • la nom simple identifie vaguement la classe, à nouveau pourrait être utile pendant toString ou des opérations de journalisation mais n'est pas garanti d'être unique.

884
2018-03-04 14:07



Ajouter des tableaux:

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

Ajoute à la réponse de Nick Holt:

[I
int[]
int[]

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

67
2018-05-08 15:06



Ajout de classes locales, lambdas et toString() méthode pour compléter les deux réponses précédentes. De plus, j'ajoute des tableaux de lambdas et des tableaux de classes anonymes (ce qui n'a aucun sens dans la pratique):

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

C'est la sortie complète:

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;

Donc, voici les règles. Tout d'abord, commençons par les types primitifs et void:

  1. Si l'objet de classe représente un type primitif ou void, toutes les quatre méthodes renvoie simplement son nom.

Maintenant, les règles pour la getName() méthode:

  1. Chaque classe ou interface non-lambda et non-array (c'est-à-dire, de niveau supérieur, imbriqué, interne, local et anonyme) a un nom (qui est retourné par getName()) qui est le nom du paquet suivi d'un point (s'il y a un paquet), suivi du nom de son fichier de classe généré par le compilateur (sans le suffixe .class). S'il n'y a pas de paquet, c'est simplement le nom du fichier de classe. Si la classe est une classe interne, imbriquée, locale ou anonyme, le compilateur doit générer au moins un $ dans son nom de fichier de classe. Notez que pour les classes anonymes, le nom de la classe se terminerait par un signe dollar suivi d'un nombre.
  2. Les noms de classe Lambda sont généralement imprévisibles, et vous ne devriez pas vous soucier d'eux de toute façon. Exactement, leur nom est le nom de la classe englobante, suivi de $$Lambda$, suivi d'un chiffre, suivi d'une barre oblique suivie d'un autre chiffre.
  3. Le descripteur de classe des primitives est Z pour boolean, B pour byte, S pour short, C pour char, I pour int, J pour long, F pour float et D pour double. Pour les classes et interfaces non-array, le descripteur de classe est L suivi de ce qui est donné par getName() suivi par ;. Pour les classes de tableau, le descripteur de classe est [ suivi du descripteur de classe du type de composant (qui peut être lui-même une autre classe de tableau).
  4. Pour les classes de tableau, le getName() méthode renvoie son descripteur de classe. Cette règle semble échouer uniquement pour les classes array dont le type est un lambda (ce qui est peut-être un bug), mais cela ne devrait pas poser de problème car il n'y a aucun point sur l'existence de classes dont le type est un lambda.

Maintenant le toString() méthode:

  1. Si l'instance de classe représente une interface (ou une annotation, qui est un type d'interface spécial), le toString() résultats "interface " + getName(). Si c'est un primitif, il revient simplement getName(). Si c'est autre chose (un type de classe, même si c'est un peu bizarre), ça revient "class " + getName().

le getCanonicalName() méthode:

  1. Pour les classes et interfaces de haut niveau, le getCanonicalName() méthode renvoie juste ce que le getName() retours de méthode.
  2. le getCanonicalName() retour de méthode null pour les classes anonymes ou locales et pour les classes de tableaux de ceux-ci.
  3. Pour les classes et interfaces internes et imbriquées, le getCanonicalName() méthode renvoie ce que le getName() La méthode remplacerait les signes dollar introduits par le compilateur par des points.
  4. Pour les classes de tableau, le getCanonicalName() retour de méthode null si le nom canonique du type de composant est null. Sinon, il renvoie le nom canonique du type de composant suivi de [].

le getSimpleName() méthode:

  1. Pour les classes de niveau supérieur, imbriquées, internes et locales, le getSimpleName() renvoie le nom de la classe tel qu'il est écrit dans le fichier source.
  2. Pour les cours anonymes getSimpleName() retourne un vide String.
  3. Pour les classes lambda le getSimpleName() retourne juste ce que le getName() retournerait sans le nom du paquet. Cela n'a pas beaucoup de sens et ressemble à un bug pour moi, mais il ne sert à rien d'appeler getSimpleName() sur une classe lambda pour commencer.
  4. Pour les classes de tableau, le getSimpleName() méthode renvoie le nom simple de la classe de composant suivi de []. Cela a l'effet secondaire drôle / bizarre que les classes de tableau dont le type de composant est une classe anonyme ont juste [] comme leurs noms simples.

60
2018-04-08 23:33



J'ai été troublé par le large éventail de systèmes de nommage différents, et j'étais sur le point de poser ma question et de répondre à cette question lorsque j'ai trouvé cette question ici. Je pense que mes résultats vont assez bien et complètent ce qui est déjà là. Mon objectif est à la recherche de Documentation sur les différents termes, et en ajoutant quelques termes plus connexes qui pourraient surgir dans d'autres endroits.

Considérez l'exemple suivant:

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • le nom simple de D est D. C'est juste la partie que vous avez écrite en déclarant la classe. Cours anonymes n'a pas de nom simple. Class.getSimpleName() renvoie ce nom ou la chaîne vide. Il est possible que le nom simple contienne un $ si vous l'écrivez comme ça, puisque $ est une partie valide d'un identifiant.

  • Selon la section JLS 6.7, tous les deux a.b.C.D et a.b.C.D.D.D serait noms complets, mais, seulement a.b.C.D serait le nom canonique de D. Ainsi, chaque nom canonique est un nom complet, mais les converges ne sont pas toujours vrais. Class.getCanonicalName() retournera le nom canonique ou null.

  • Class.getName() est documenté pour retourner le nom binaire, comme spécifié dans JLS section 13.1. Dans ce cas, il retourne a.b.C$D pour D et [La.b.C$D; pour D[].

  • Cette réponse démontre qu'il est possible que deux classes chargées par le même chargeur de classes aient le même nom canonique mais distinct noms binaires. Aucun nom n'est suffisant pour déduire de façon fiable l'autre: si vous avez le nom canonique, vous ne savez pas quelles parties du nom sont des paquets et lesquelles contiennent des classes. Si vous avez le nom binaire, vous ne savez pas $ ont été introduits comme séparateurs et qui faisaient partie d'un nom simple.

  • Cours anonymes et classes locales n'a pas noms complets mais ont encore nom binaire. La même chose vaut pour les classes imbriquées dans ces classes. Chaque La classe a un nom binaire.

  • Fonctionnement javap -v -private sur a/b/C.class montre que le bytecode se réfère au type de d comme La/b/C$D; et celle du tableau ds comme [La/b/C$D;. Ils sont appelés descripteurset ils sont spécifiés dans JVMS section 4.3.

  • Le nom de la classe a/b/C$D utilisé dans ces deux descripteurs est ce que vous obtenez en remplaçant . par / dans le nom binaire. La spécification JVM appelle apparemment cela le forme interne du nom binaire. JVMS section 4.2.1 le décrit, et déclare que la différence par rapport au nom binaire était pour des raisons historiques.

  • le nom de fichier d'une classe dans l'un des chargeurs de classe basé sur un nom de fichier typique est ce que vous obtenez si vous interprétez le / dans la forme interne du nom binaire en tant que séparateur de répertoire et ajoutez l'extension de nom de fichier .class à lui. Il est résolu par rapport au chemin de classe utilisé par le chargeur de classe en question.


6
2018-05-02 09:15



c'est le meilleur document que j'ai trouvé décrivant 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



Il est intéressant de noter que getCanonicalName() et getSimpleName() peut élever InternalError lorsque le nom de la classe est mal formé. Cela se produit pour certains langages JVM non Java, par exemple, Scala.

Considérez ce qui suit (Scala 2.11 sur 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

Cela peut être un problème pour des environnements de langage mixtes ou des environnements qui chargent de manière dynamique le bytecode, par exemple des serveurs d'applications et d'autres logiciels de plate-forme.


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