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.
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.
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.
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.
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.
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
:
void
, all the four methods simply returns its name.Now the rules for the getName()
method:
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.$$Lambda$
, followed by a number, followed by a slash, followed by another number.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).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:
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:
getCanonicalName()
method returns just what the getName()
method returns.getCanonicalName()
method returns null
for anonymous or local classes and for array classes of those.getCanonicalName()
method returns what the getName()
method would replacing the compiler-introduced dollar-signs by dots.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:
getSimpleName()
returns the name of the class as written in the source file.getSimpleName()
returns an empty String
.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.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.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 defaultClassLoader
. Within the scope of a certainClassLoader
, 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 thejavac
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 aClassLoader
. (In hindsight, a better name for this getter would have beengetJavaName
; 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.
andExample 6.7-2.
goes overFully Qualified Names
andFully Qualified Names v. Canonical Name
respectively
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With