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

后端 未结 8 1179
感动是毒
感动是毒 2020-11-27 08:58

In Java, what is the difference between these:

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


        
8条回答
  •  执笔经年
    2020-11-27 09:26

    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.

提交回复
热议问题