Differencebetween Canonical Name, Simple Name and Class Name in Java Class

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

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 default ClassLoader. Within the scope of a certain ClassLoader, 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 the javac 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 a ClassLoader. (In hindsight, a better name for this getter would have been getJavaName; 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. and Example 6.7-2. goes over Fully Qualified Names and Fully Qualified Names v. Canonical Name respectively

What's the difference between Name and CanonicalName?

Consider the following program:

package org.test.stackoverflow;

public class CanonicalName {

public static void main(String[] args) {
CanonicalName cn = new CanonicalName();
cn.printClassNames();
}

private Anonymous anony;
private MyAnony myAnony;

public CanonicalName() {
anony = new Anonymous() {
public void printInterface() {
System.out.println("Anony Name: " + getClass().getName());
System.out.println("Anony CanonicalName: " + getClass().getCanonicalName());
}
};
myAnony = new MyAnony();
}

public void printClassNames() {
System.out.println("CanonicalName, Name: " + getClass().getName());
System.out.println("CanonicalName, CanonicalName: " + getClass().getCanonicalName());
anony.printInterface();
myAnony.printInterface();
}

private static interface Anonymous {
public void printInterface();
}

private static class MyAnony implements Anonymous {
public void printInterface() {
System.out.println("MyAnony Name: " + getClass().getName());
System.out.println("MyAnony CanonicalName: " + getClass().getCanonicalName());
}
}
}

Output:

CanonicalName, Name: org.test.stackoverflow.CanonicalName
CanonicalName, CanonicalName: org.test.stackoverflow.CanonicalName
Anony Name: org.test.stackoverflow.CanonicalName$1
Anony CanonicalName: null
MyAnony Name: org.test.stackoverflow.CanonicalName$MyAnony
MyAnony CanonicalName: org.test.stackoverflow.CanonicalName.MyAnony

So it seems that for base classes, they return the same thing. For inner classes, getName() uses the $ naming convention (i.e. what is used for .class files), and getCanonicalName() returns what you would use if you were trying to instantiate the class. You couldn't do that with a (little-a) anonymous class, so that's why getCanonicalName() returns null.

What is the formulation for Java Array class names?

One letter for each primitive, one symbol for arrays, and one letter for reference:

  • I = int
  • J = long
  • D = double
  • F = float
  • Z = boolean
  • C = char
  • B = byte
  • S = short
  • Lcom/foo/Thing; = reference 'com.foo.Thing'
  • [ = array of whatever is next (so, [[I is an int[][]).

This is VM-speak for type signatures. For example, the signature of:

public boolean[] foo(String[] args, int count) { ... }

is: ([Ljava/lang/String;I)[Z.

It is for machines and not humans; it is easy to parse (you just move forward, no need to look-ahead).

This is not the 'class name' of a thing; the usual name for this construct is 'vm name'. Note that generics just disappear from these; the VM name of List<String> is Ljava/util/List;. It's why you can't override methods if the two methods end up with the same name, param types, and return type if you remove all generics.

Should I use a class name when referencing a static field in that class?

I agree with whatever being mentioned in the comments. Apart from this, I keep readability of the code in mind and then decide. Following code fragment gives an idea on the same:

public class Engine {
private static boolean isOn = false;

public static void doWork1() {
if (isOn) { // check if engine is on
// do some work
}
}

public static void doWork2() {
if (Engine.isOn) {
// do some work
}
}
...
}

You can see that doWork2() method by using the class name increased the code readability even without adding any comment to the code.

Java code snippet to shorten package name from fully qualified class name

You can use NameAbbreviator from log4j-core.

NameAbbreviator n = NameAbbreviator.getAbbreviator("1.");
StringBuilder sb = new StringBuilder();
n.abbreviate("com.test.Student", sb);
System.out.println(sb); /* Result: c.t.Student */


Related Topics



Leave a reply



Submit