Javaでは、これらの違いは何ですか。
Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();
私はJavadocを何度もチェックしましたが、これでうまく説明できません。私もテストを実行しましたが、これらのメソッドが呼び出される方法の背後にある本当の意味を反映していませんでした。
Javaでは、これらの違いは何ですか。
Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();
私はJavadocを何度もチェックしましたが、これでうまく説明できません。私もテストを実行しましたが、これらのメソッドが呼び出される方法の背後にある本当の意味を反映していませんでした。
何かわからない場合は、最初にテストを書いてみてください。
これは私がしました:
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();
}
}
プリント:
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
getSimpleName
空の文字列を返す最後のブロックに空のエントリがあります。
これを見た結果は次のとおりです。
- nameは、クラスを動的にロードするために使用する名前です。たとえば、
Class.forName
デフォルトのを使用してを呼び出しますClassLoader
。特定のスコープ内ではClassLoader
、すべてのクラスに一意の名前があります。- 正規名は、インポートステートメントで使用される名前です。
toString
これは、操作中またはログ操作中に役立つ場合があります。javac
コンパイラがクラスパスを完全に表示すると、コンパイル時に完全修飾クラス名とパッケージ名を衝突させることにより、コンパイラ内の正規名の一意性を強制します。ただし、JVMはそのような名前の衝突を受け入れる必要があるため、正規の名前は内のクラスを一意に識別しませんClassLoader
。(後から考えると、このゲッターのより適切な名前はgetJavaName
;ですが、このメソッドは、JVMがJavaプログラムを実行するためだけに使用されていた時代にさかのぼります。)- 単純な名前はクラスを大まかに識別します。これも、操作中またはロギング操作中に役立つ場合があり
toString
ますが、一意であるとは限りません。- タイプ名は、「このタイプの名前の情報文字列」、「
toString
次のようなものです。純粋に情報であり、コントラクト値はありません」を返します。(sir4ur0nによって書かれたように)
また、これらのタイプの技術的なJava APIの詳細については、一般的にJava言語仕様のドキュメントを参照できます。
- この主題に関する
Java 11
仕様は次のとおりです。https ://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7
Example 6.7-2.
とそれぞれExample 6.7-2.
行きますFully Qualified Names
Fully Qualified Names v. Canonical Name
ローカルクラス、ラムダ、およびtoString()
メソッドを追加して、前の2つの回答を完成させます。さらに、ラムダの配列と無名クラスの配列を追加します(実際には意味がありません)。
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 {}
これは完全な出力です:
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;
だから、ここにルールがあります。まず、プリミティブ型から始めましょうvoid
。
void
4つのメソッドすべてがその名前を返すだけです。getName()
ここで、メソッドのルールは次のとおりです。
getName()
は、パッケージ名の後にドット(パッケージがある場合)が続く名前(によって返される)があります。 )の後に、コンパイラによって生成されたクラスファイルの名前が続きます(接尾辞なし.class
)。パッケージがない場合は、単にクラスファイルの名前です。$
クラスが内部クラス、ネストされたクラス、ローカルクラス、または匿名クラスの場合、コンパイラはクラスファイル名に少なくとも1つを生成する必要があります。匿名クラスの場合、クラス名はドル記号とそれに続く数字で終わることに注意してください。$$Lambda$
、その後に、数字、スラッシュ、別の数字が続きます。Z
for boolean
、B
for byte
、S
for short
、C
for char
、I
for int
、J
for long
、F
for 、float
およびD
fordouble
です。非配列クラスおよびインターフェースの場合、クラス記述子のL
後には、で指定されたものが続き、その後に。がgetName()
続き;
ます。配列クラスの場合、クラス記述子[
の後にコンポーネントタイプのクラス記述子が続きます(それ自体が別の配列クラスである場合があります)。getName()
メソッドはそのクラス記述子を返します。このルールは、コンポーネントタイプがラムダである配列クラス(バグである可能性があります)に対してのみ失敗するようですが、コンポーネントタイプがラムダである配列クラスが存在しても意味がないため、これが問題にならないことを願っています。さて、toString()
メソッド:
toString()
返します"interface " + getName()
。プリミティブの場合は、単純にを返しますgetName()
。それが何か他のものである場合(それがかなり奇妙なものであっても、クラスタイプ)、それはを返します"class " + getName()
。getCanonicalName()
方法:
getCanonicalName()
メソッドはメソッドが返すものだけをgetName()
返します。getCanonicalName()
メソッドはnull
、匿名クラスまたはローカルクラス、およびそれらの配列クラスを返します。getCanonicalName()
メソッドを返します。getName()
getCanonicalName()
メソッドは戻ります。それ以外の場合は、コンポーネントタイプの正規名の後に。が続きます。null
null
[]
getSimpleName()
方法:
getSimpleName()
はソースファイルに記述されているクラスの名前を返します。getSimpleName()
は空を返しますString
。getSimpleName()
返されるものを返すだけです。getName()
これはあまり意味がなく、私にとってはバグのように見えますが、getSimpleName()
最初にラムダクラスを呼び出す意味はありません。getSimpleName()
メソッドはコンポーネントクラスの単純な名前の後に。を返します[]
。[]
これには、コンポーネントタイプが匿名クラスである配列クラスが単純な名前と同じように持つ面白い/奇妙な副作用があります。Nick Holtの観察に加えて、Array
データ型についていくつかのケースを実行しました。
//primitive Array
int demo[] = new int[5];
Class<? extends int[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());
System.out.println();
//Object Array
Integer demo[] = new Integer[5];
Class<? extends Integer[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());
上記のコードスニペットは次のように出力します。
[I
int[]
int[]
[Ljava.lang.Integer;
java.lang.Integer[]
Integer[]
さまざまな命名スキームにも混乱していて、ここでこの質問を見つけたとき、これについて自分自身の質問をして答えようとしていました。私の調査結果はそれに十分に適合し、すでにここにあるものを補完すると思います。私の焦点は、さまざまな用語に関するドキュメントを探し、他の場所で発生する可能性のある関連用語を追加することです。
次の例を考えてみましょう。
package a.b;
class C {
static class D extends C {
}
D d;
D[] ds;
}
の単純な名前はD
ですD
。これは、クラスを宣言するときに書いた部分です。匿名クラスには単純な名前はありません。Class.getSimpleName()
この名前または空の文字列を返します。JLSセクション3.8によると、は識別子の有効な部分である$
ため、このように記述した場合、単純な名前にaを含めることができます(多少推奨されない場合でも)。$
JLSセクション6.7によると、a.b.C.D
とa.b.C.D.D.D
は両方とも完全修飾名ですが、の正規名のみにa.b.C.D
なります。したがって、すべての正規名は完全修飾名ですが、その逆は常に正しいとは限りません。正規名またはを返します。D
Class.getCanonicalName()
null
Class.getName()
JLSセクション13.1で指定されているように、バイナリ名を返すように文書化されています。この場合、forとforを返します。a.b.C$D
D
[La.b.C$D;
D[]
この回答は、同じクラスローダーによってロードされた2つのクラスが、同じ正規名であるが異なるバイナリ名を持つ可能性があることを示しています。どちらの名前も、もう一方を確実に推測するには十分ではありません。正規名がある場合、名前のどの部分がパッケージで、どの部分にクラスが含まれているかがわかりません。$
バイナリ名を持っている場合、どれが区切り文字として導入され、どれが単純な名前の一部であったかはわかりません。(クラスファイルには、クラス自体とそれを囲むクラスのバイナリ名が格納されているため、ランタイムでこの区別を行うことができます。)
匿名クラスとローカルクラスには完全修飾名はありませんが、バイナリ名はあります。そのようなクラス内にネストされたクラスについても同じことが言えます。すべてのクラスにはバイナリ名があります。
で実行javap -v -private
するa/b/C.class
と、バイトコードがasのタイプを参照し、配列のタイプをd
として参照することを示します。これらは記述子と呼ばれ、 JVMセクション4.3で指定されています。La/b/C$D;
ds
[La/b/C$D;
a/b/C$D
これらの記述子の両方で使用されるクラス名は、バイナリ名で置き換えること.
によって取得されるものです。/
JVM仕様では、これをバイナリ名の内部形式と呼んでいるようです。JVMSセクション4.2.1はそれを説明し、バイナリ名との違いは歴史的な理由によるものであると述べています。
一般的なファイル名ベースのクラスローダーの1つにあるクラスのファイル名/
は、バイナリ名の内部形式をディレクトリセパレータとして解釈し、それにファイル名拡張子.class
を追加すると得られるものです。問題のクラスローダーによって使用されるクラスパスに関連して解決されます。
これは、getName()、getSimpleName()、getCanonicalName()について説明している私が見つけた最高のドキュメントです。
// Primitive type
int.class.getName(); // -> int
int.class.getCanonicalName(); // -> int
int.class.getSimpleName(); // -> int
// Standard class
Integer.class.getName(); // -> java.lang.Integer
Integer.class.getCanonicalName(); // -> java.lang.Integer
Integer.class.getSimpleName(); // -> Integer
// Inner class
Map.Entry.class.getName(); // -> java.util.Map$Entry
Map.Entry.class.getCanonicalName(); // -> java.util.Map.Entry
Map.Entry.class.getSimpleName(); // -> Entry
// Anonymous inner class
Class<?> anonymousInnerClass = new Cloneable() {}.getClass();
anonymousInnerClass.getName(); // -> somepackage.SomeClass$1
anonymousInnerClass.getCanonicalName(); // -> null
anonymousInnerClass.getSimpleName(); // -> // An empty string
// Array of primitives
Class<?> primitiveArrayClass = new int[0].getClass();
primitiveArrayClass.getName(); // -> [I
primitiveArrayClass.getCanonicalName(); // -> int[]
primitiveArrayClass.getSimpleName(); // -> int[]
// Array of objects
Class<?> objectArrayClass = new Integer[0].getClass();
objectArrayClass.getName(); // -> [Ljava.lang.Integer;
objectArrayClass.getCanonicalName(); // -> java.lang.Integer[]
objectArrayClass.getSimpleName(); // -> Integer[]
興味深いことに、クラス名が不正な場合に発生する可能性がgetCanonicalName()
あります。これは、Scalaなどの一部の非JavaJVM言語で発生します。getSimpleName()
InternalError
次のことを考慮してください(Java8のScala2.11):
scala> case class C()
defined class C
scala> val c = C()
c: C = C()
scala> c.getClass.getSimpleName
java.lang.InternalError: Malformed class name
at java.lang.Class.getSimpleName(Class.java:1330)
... 32 elided
scala> c.getClass.getCanonicalName
java.lang.InternalError: Malformed class name
at java.lang.Class.getSimpleName(Class.java:1330)
at java.lang.Class.getCanonicalName(Class.java:1399)
... 32 elided
scala> c.getClass.getName
res2: String = C
これは、混合言語環境や、アプリサーバーやその他のプラットフォームソフトウェアなど、バイトコードを動的にロードする環境で問題になる可能性があります。
getName() –このClassオブジェクトによって表されるエンティティの名前(クラス、インターフェイス、配列クラス、プリミティブ型、またはvoid)を文字列として返します。
getCanonicalName() –Java言語仕様で定義されている基になるクラスの正規名を返します。
getSimpleName() –基になるクラスの単純な名前、つまりソースコードで指定された名前を返します。
package com.practice;
public class ClassName {
public static void main(String[] args) {
ClassName c = new ClassName();
Class cls = c.getClass();
// returns the canonical name of the underlying class if it exists
System.out.println("Class = " + cls.getCanonicalName()); //Class = com.practice.ClassName
System.out.println("Class = " + cls.getName()); //Class = com.practice.ClassName
System.out.println("Class = " + cls.getSimpleName()); //Class = ClassName
System.out.println("Class = " + Map.Entry.class.getName()); // -> Class = java.util.Map$Entry
System.out.println("Class = " + Map.Entry.class.getCanonicalName()); // -> Class = java.util.Map.Entry
System.out.println("Class = " + Map.Entry.class.getSimpleName()); // -> Class = Entry
}
}
1つの違いは、匿名クラスを使用する場合、を使用してクラスの名前を取得しようとするとnull値を取得できることです。getCanonicalName()
もう1つの事実は、メソッドの動作が内部クラスのメソッドとgetName()
は異なることです。囲んでいるクラスの正規名と内部クラスの単純名の間の区切り文字としてドルを使用します。getCanonicalName()
getName()
Javaでのクラス名の取得について詳しく知るため。
public void printReflectionClassNames(){
StringBuffer buffer = new StringBuffer();
Class clazz= buffer.getClass();
System.out.println("Reflection on String Buffer Class");
System.out.println("Name: "+clazz.getName());
System.out.println("Simple Name: "+clazz.getSimpleName());
System.out.println("Canonical Name: "+clazz.getCanonicalName());
System.out.println("Type Name: "+clazz.getTypeName());
}
outputs:
Reflection on String Buffer Class
Name: java.lang.StringBuffer
Simple Name: StringBuffer
Canonical Name: java.lang.StringBuffer
Type Name: java.lang.StringBuffer