How do getName, getSimpleName, and getCanonicalName differ on a Class object?
answer
- getName = binary: $ for nested, [L...; for arrays
- getSimpleName = short, no package; empty for anonymous
- getCanonicalName = dotted source name, NULL for local/anonymous/their arrays
- forName round-trips with getName, not canonical
- getTypeName is the never-null readable alternative
basics
~20 sgetName() gives the JVM's internal name (with $ for nested classes and special codes for arrays). getSimpleName() gives just the short class name without any package. getCanonicalName() gives the dotted source-code name you would write to import it, or null when none exists.
solid answer
~40 sAll three describe the same class differently. getName() returns the binary/JVM name: dotted package plus the class, using a dollar sign for nested classes (Outer$Inner) and the JVM array encoding ([Ljava.lang.String; for String[]). getSimpleName() returns only the short name with no package and no enclosing class (Inner), an empty string for anonymous classes, and appends [] for arrays. getCanonicalName() returns the human, source-level name you would write in an import or fully-qualified reference (Outer.Inner, String[]); it returns null for things that have no canonical name, namely local classes, anonymous classes, and arrays of such. The practical rule: use getName() for serialization, logging, and reflective round-trips with forName; use getSimpleName() for display; and treat getCanonicalName() as possibly null.
code
java · 17 linesclass Outer {
static class Inner {}
}
Outer.Inner.class.getName(); // com.example.Outer$Inner
Outer.Inner.class.getSimpleName(); // Inner
Outer.Inner.class.getCanonicalName(); // com.example.Outer.Inner
String[].class.getName(); // [Ljava.lang.String;
String[].class.getSimpleName(); // String[]
String[].class.getCanonicalName(); // java.lang.String[]
Runnable r = () -> {};
// an anonymous/lambda-style class:
Object anon = new Object() {};
anon.getClass().getSimpleName(); // "" (empty)
anon.getClass().getCanonicalName(); // nullgo deeper
Know that getSimpleName is the short name and getName is the full name; mention getCanonicalName looks like an import.
Distinguish all three precisely, including $ vs dots and the null/empty cases for anonymous classes.
Explain array encodings, the round-trip-with-forName rule, the canonical-name null cases, and when to prefer getTypeName.
Discuss why the binary name is the stable serialization identity, how generated names for anonymous/local classes are unstable across compilers, and the resulting brittleness for persisted type references.
## The problem: one class, several names A `Class` object has three name accessors that look similar but answer different questions. The differences only become visible for **nested classes, inner/local/anonymous classes, and arrays** — for an ordinary top-level class all three (modulo array brackets) agree. First, some vocabulary: - **Top-level class:** declared directly in a package, not inside another class. - **Nested class:** declared inside another class. A `static` nested class or a non-static **inner** class. - **Local class:** a named class declared inside a method. - **Anonymous class:** a class with no name, created with `new SomeType() { ... }`. - **Binary name / JVM name:** the name the class file format and class loaders use internally. - **Canonical name:** the dotted name exactly as you would write it in Java source to refer to the type. ### `getName()` — the binary (JVM) name This is the name the JVM and class loaders use; it is what you must pass to `Class.forName`. - Top-level: `com.example.Foo`. - Nested/inner: uses a **dollar sign** between enclosing and nested: `com.example.Outer$Inner`. - Local/anonymous: includes a generated tag, e.g. `com.example.Outer$1` (anonymous) or `com.example.Outer$1Local`. - **Arrays** use the JVM descriptor encoding: - `int[]` → `[I` - `String[]` → `[Ljava.lang.String;` - `String[][]` → `[[Ljava.lang.String;` - each leading `[` is one dimension; `L...;` wraps a reference type; primitives have single-letter codes (`I` int, `Z` boolean, `J` long, `D` double, etc.). ### `getSimpleName()` — the short display name Just the class's own short name, no package, no enclosing class. - Top-level `Foo` → `"Foo"`. - `Outer.Inner` → `"Inner"`. - **Anonymous class → empty string `""`** (it has no name). - **Arrays → element simple name + `[]`**: `String[]` → `"String[]"`, `int[][]` → `"int[][]"`. Use this for user-facing or log-friendly labels. ### `getCanonicalName()` — the source-level name The name you would type to import or reference the type in source. - Top-level: `com.example.Foo`. - Nested/inner: uses **dots**, not dollars: `com.example.Outer.Inner`. - **Arrays use dotted element + `[]`**: `String[]` → `java.lang.String[]`. - **Returns `null`** when there is no canonical name: **local classes, anonymous classes, and arrays whose element type has no canonical name.** This null is the classic gotcha — always guard it. ### Side-by-side | Class | getName() | getSimpleName() | getCanonicalName() | |---|---|---|---| | `com.example.Foo` | `com.example.Foo` | `Foo` | `com.example.Foo` | | `Outer.Inner` | `com.example.Outer$Inner` | `Inner` | `com.example.Outer.Inner` | | anonymous | `com.example.Outer$1` | `""` (empty) | `null` | | local class | `com.example.Outer$1Local` | `Local` | `null` | | `int[]` | `[I` | `int[]` | `int[]` | | `String[]` | `[Ljava.lang.String;` | `String[]` | `java.lang.String[]` | ### Practical guidance - **Round-tripping with `forName`:** use `getName()` — `Class.forName(getName())` works (forName understands `$` and the array encoding); the canonical/simple names do **not** reliably round-trip. - **Display/logging:** prefer `getSimpleName()`. - **Anything user-facing from `getCanonicalName()`:** null-check it (fall back to `getName()` or `getSimpleName()`). - There is also `getTypeName()` (Java 8+), which gives a readable name like `java.lang.String[]` and is never null — handy as a non-null alternative.
- Which name accessor returns null, and for which kinds of class?getCanonicalName() returns null for local classes, anonymous classes, and arrays whose component type lacks a canonical name. getName() and getSimpleName() are never null (getSimpleName can be empty for anonymous classes).
- If you serialize a class name to recreate it later via Class.forName, which accessor should you store?getName(), the binary name. It uses $ for nested classes and the JVM array encoding, both of which Class.forName understands; canonical and simple names do not round-trip reliably.
saying these in an interview costs you the question
- Assuming getCanonicalName never returns null
- Believing getName uses dots between outer and nested classes (it uses $)
- Thinking getSimpleName returns the package-qualified name
- Expecting getName of an array to read like String[] (it is [Ljava.lang.String;)