What is the difference between getCanonicalName() and getName()?

Asked

Viewed 677 times

5

To return the package name with the class name I always used the method getName(). However in that reply of @Articuno of the question: Customize the eclipse Generate toString() to print the path of a class, it showed a different way to do this, but using the method getCanonicalName(), that in the end returns the same result.

See just one example customizing the method toString():

@Override
public String toString() {
    return this.getClass().getName();
}

Which apparently would be equivalent to:

@Override
public String toString() {
    return this.getClass().getCanonicalName();
}

What is the real difference between the methods getCanonicalName() and getName()?

1 answer

9


TL;DR

The difference between the getName() and the getCanonicalName() are those:

  • In arrays classes, the getName() will return [ followed by getName() of the base component. The getCanonicalName() will return the getCanonicalName() of the base component followed by [].

  • In nested classes and internal classes, the getName() will bring the name with some $ introduced by the compiler in the middle. The getCanonicalName() will bring that name with the $ replaced by a ..

  • In local classes and anonymous classes (and arrays of these), the getCanonicalName() returns null, while the getName() nay.

Detailed answer

Based on my answer on Soen:

This code below shows the differences between getName(), getCanonicalName(), getSimpleName() and toString() for classes, interfaces, primitive types, arrays classes, nested classes, internal classes, anonymous classes, local classes and Ambdes classes.

In addition, I also add classes that are anonymous class arrays and Ambdas arrays (which however make no sense in practice):

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()); // Obtém uma classe array cujo componente-base seja um lambda.
    }

    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()); // Obtém uma classe array cujo componente-base seja uma classe anônima.
        x(() -> {});
    }
}

enum SomeEnum {
   BLUE, YELLOW, RED;
}

@interface SomeAnnotation {}

Here’s the complete exit:

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 these are the rules. First, let’s start with the primitive types and with the void:

  1. If the class object represents a primitive type or void, all four methods will return your name.

Now, the rules for the method getName():

  1. Each non-lambda and non-array class or interface (i.e., top-level, nested, internal, local or anonymous) has a name (returned by getName()) which consists of the package name followed by a dot (if there is a package), followed by the class file name as generated by the compiler (without the suffix .class). If there is no package, this is simply the name of the class file. If it is an internal, nested, local or anonymous class, the compiler must generate at least one $ in your name. Note that for anonymous classes, the class name will end with a dollar sign followed by a number.

  2. Lambda class names are generally unpredictable, and you shouldn’t even care about them. Precisely, their name is the name of the class that contains them, followed by $$Lambda$, followed by a number, followed by a bar and followed by a number.

  3. The class descriptor for primitive types is 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 classes and non-arrays interfaces, the class descriptor is composed of a L followed by what is returned by getName() and followed by a ;. For array classes, the descriptor is a [ followed by the class descriptor which is the base component of the array (which in turn can be another array class).

  4. For array classes, the method getName() returns the class descriptor. This rule seems to fail only for array classes whose base component is a lambda (which is possibly a bug), but it probably doesn’t matter because it doesn’t even make sense to have array classes whose base component is a lambda.

Now, the method toString():

  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’s a primitive type, it simply returns getName(). If it is anything else (a class type, even if it is a very strange one), it returns "class " + getName().

The method getCanonicalName():

  1. For upper-level classes and interfaces, the method getCanonicalName() simply returns what the getName() returns.

  2. The method getCanonicalName() returns null for anonymous classes and local classes and for arrays of these classes.

  3. For nested and internal classes and interfaces, the method getCanonicalName() returns what the getName() would return by replacing the tokens entered by the compiler with dots.

  4. For arrays classes, the method getCanonicalName() returns null if the canonical name of the component class is null. Otherwise, it returns the canonical name of the component class followed by [].

The method getSimpleName():

  1. For nested, internal or local upper-level classes and interfaces, getSimpleName() returns the class or interface name as written in the source code.

  2. For anonymous classes, the getSimpleName() returns a String empty.

  3. For lambda classes, the getSimpleName() returns only what the getName() would return without the package name. This makes no sense and I believe it is a bug. However, it doesn’t even make sense for you to call getSimpleName() in a lambda class for conversation starters.

  4. For array classes, the method getSimpleName() returns the simple name of the component-base class followed by []. This has the curious/strange side effect that array classes whose base component is an anonymous class will have only [] as their simple names.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.