Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between canonical name, simple name and class name in Java Class?

Tags:

java

In Java, what is the difference between these:

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

I have checked the Javadoc multiple times and yet this never explains it well. I also ran a test and that didn't reflect any real meaning behind the way these methods are called.

like image 742
Mohamed Taher Alrefaie Avatar asked Mar 04 '13 13:03

Mohamed Taher Alrefaie


People also ask

What is a canonical name in Java?

Canonical name of a Java class is the name of the class along with the package. For example, the canonical name of the class File is java. io. File. You can also get the canonical name of a particular class using Java method.

What is a simple name in Java?

The getSimpleName() method of java Class class returns the simple name of the underlying class as given in the source code. In the case of an anonymous class, it returns an empty string.

What is simple name of class?

getSimpleName() returns the simple name of the underlying class as given in the source code. Returns an empty string if the underlying class is anonymous.

Which name is same as class name in Java?

Every class object is created using the same new keyword, so it must have information about the class to which it must create an object. For this reason, the constructor name should be the same as the class name.


2 Answers

Adding local classes, lambdas and the toString() method to complete the previous two answers. Further, I add arrays of lambdas and arrays of anonymous classes (which do not make any sense in practice though):

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

This is the full output:

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; 

So, here are the rules. First, lets start with primitive types and void:

  1. If the class object represents a primitive type or void, all the four methods simply returns its name.

Now the rules for the getName() method:

  1. Every non-lambda and non-array class or interface (i.e, top-level, nested, inner, local and anonymous) has a name (which is returned by getName()) that is the package name followed by a dot (if there is a package), followed by the name of its class-file as generated by the compiler (whithout the suffix .class). If there is no package, it is simply the name of the class-file. If the class is an inner, nested, local or anonymous class, the compiler should generate at least one $ in its class-file name. Note that for anonymous classes, the class name would end with a dollar-sign followed by a number.
  2. Lambda class names are generally unpredictable, and you shouldn't care about they anyway. Exactly, their name is the name of the enclosing class, followed by $$Lambda$, followed by a number, followed by a slash, followed by another number.
  3. The class descriptor of the primitives are Z for boolean, B for byte, S for short, C for char, I for int, J for long, F for float and D for double. For non-array classes and interfaces the class descriptor is L followed by what is given by getName() followed by ;. For array classes, the class descriptor is [ followed by the class descriptor of the component type (which may be itself another array class).
  4. For array classes, the getName() method returns its class descriptor. This rule seems to fail only for array classes whose the component type is a lambda (which possibly is a bug), but hopefully this should not matter anyway because there is no point even on the existence of array classes whose component type is a lambda.

Now, the toString() method:

  1. If the class instance represents an interface (or an annotation, which is a special type of interface), the toString() returns "interface " + getName(). If it is a primitive, it returns simply getName(). If it is something else (a class type, even if it is a pretty weird one), it returns "class " + getName().

The getCanonicalName() method:

  1. For top-level classes and interfaces, the getCanonicalName() method returns just what the getName() method returns.
  2. The getCanonicalName() method returns null for anonymous or local classes and for array classes of those.
  3. For inner and nested classes and interfaces, the getCanonicalName() method returns what the getName() method would replacing the compiler-introduced dollar-signs by dots.
  4. For array classes, the getCanonicalName() method returns null if the canonical name of the component type is null. Otherwise, it returns the canonical name of the component type followed by [].

The getSimpleName() method:

  1. For top-level, nested, inner and local classes, the getSimpleName() returns the name of the class as written in the source file.
  2. For anonymous classes the getSimpleName() returns an empty String.
  3. For lambda classes the getSimpleName() just returns what the getName() would return without the package name. This do not makes much sense and looks like a bug for me, but there is no point in calling getSimpleName() on a lambda class to start with.
  4. For array classes the getSimpleName() method returns the simple name of the component class followed by []. This have the funny/weird side-effect that array classes whose component type is an anonymous class have just [] as their simple names.
like image 23
Victor Stafusa Avatar answered Oct 17 '22 22:10

Victor Stafusa


If you're unsure about something, try writing a test first.

I did this:

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

Prints:

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 

There's an empty entry in the last block where getSimpleName returns an empty string.

The upshot looking at this is:

  • the name is the name that you'd use to dynamically load the class with, for example, a call to Class.forName with the default ClassLoader. Within the scope of a certain ClassLoader, all classes have unique names.
  • the canonical name is the name that would be used in an import statement. It might be useful during toString or logging operations. When the javac compiler has complete view of a classpath, it enforces uniqueness of canonical names within it by clashing fully qualified class and package names at compile time. However JVMs must accept such name clashes, and thus canonical names do not uniquely identify classes within a ClassLoader. (In hindsight, a better name for this getter would have been getJavaName; but this method dates from a time when the JVM was used solely to run Java programs.)
  • the simple name loosely identifies the class, again might be useful during toString or logging operations but is not guaranteed to be unique.
  • the type name returns "an informative string for the name of this type", "It's like toString: it's purely informative and has no contract value". (as written by sir4ur0n)

Also you can commonly reference the Java Language Specification documentation for these types technical Java API details:

  • Here's the Java 11 Specification on this subject matter: https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7

Example 6.7-2. and Example 6.7-2. goes over Fully Qualified Names and Fully Qualified Names v. Canonical Name respectively

like image 87
Nick Holt Avatar answered Oct 17 '22 22:10

Nick Holt