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
:
- 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:
- 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.
- 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.
- 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).
- 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:
- 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:
- For top-level classes and interfaces, the
getCanonicalName()
method returns just what the getName()
method returns.
- The
getCanonicalName()
method returns null
for anonymous or local classes and for array classes of those.
- For inner and nested classes and interfaces, the
getCanonicalName()
method returns what the getName()
method would replacing the compiler-introduced dollar-signs by dots.
- 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:
- For top-level, nested, inner and local classes, the
getSimpleName()
returns the name of the class as written in the source file.
- For anonymous classes the
getSimpleName()
returns an empty String
.
- 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.
- 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.