Javaクラスの正規名、単純名、クラス名の違いは何ですか?


973

Javaでは、これらの違いは何ですか:

Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();

私は何度もJavadocをチェックしましたが、これではうまく説明できません。私もテストを実行しましたが、これらのメソッドの呼び出し方法の背後にある実際の意味は反映されていませんでした。



218
これは合理的な質問だと思います。javadocは、3つの違いを説明するのに適していません。
Graham Borland 2013年

1
-docs.oracle.com/javase/6/docs/api/java/lang/Class.htmlを参照するか、テストを作成してください。
Nick Holt

7
@GrahamBorland javadocは「Java言語仕様で定義されているとおりと言っているため、そのドキュメントで調べることができます。それがクリック可能なリンクではないからといって、人々はまだ最小限の努力で最初の検索エンジンの結果をクリックすることができます。
vbence 2014年

66
@vbence:ほとんどの人は、JLSを調べてこのような些細なことを行うよりも、物事を成し遂げたいと思っています。したがって、これは最初のGoogleの結果です:)
pathikrit 2014年

回答:


1130

不明な点がある場合は、まずテストを作成してみてください。

これは私がしました:

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(プリミティブ):
    getName():int
    getCanonicalName():int
    getSimpleName():int
    getTypeName():int

String.class(通常のクラス):
    getName():java.lang.String
    getCanonicalName():java.lang.String
    getSimpleName():String
    getTypeName():java.lang.String

java.util.HashMap.SimpleEntry.class(ネストされたクラス):
    getName():java.util.AbstractMap $ SimpleEntry
    getCanonicalName():java.util.AbstractMap.SimpleEntry
    getSimpleName():SimpleEntry
    getTypeName():java.util.AbstractMap $ SimpleEntry

新しいjava.io.Serializable(){}。getClass()(匿名の内部クラス):
    getName():ClassNameTest $ 1
    getCanonicalName():null
    getSimpleName():    
    getTypeName():ClassNameTest $ 1

getSimpleName空の文字列を返す最後のブロックに空のエントリがあります。

これを見た結果は次のとおりです。

  • 名前はあなたが動的に、例えば、電話を持つクラスをロードするために使用したいという名前でClass.forName、デフォルトでClassLoader。特定ののスコープ内ではClassLoader、すべてのクラスに一意の名前があります。
  • 正規名は import文で使用される名前です。これは、toString操作中またはログの記録中に役立つ場合があります。javacコンパイラーがクラスパスの完全なビューを持っている場合、コンパイラーは、コンパイル時に完全修飾クラス名とパッケージ名を衝突させることにより、その中に正規名の一意性を適用します。ただし、JVMはこのような名前の衝突を受け入れる必要があるため、正規名は内のクラスを一意に識別しませんClassLoader。(後から考えると、このゲッターのより良い名前はgetJavaNameでしたが、このメソッドは、JVMがJavaプログラムを実行するためだけに使用されたときから遡ります。)
  • 単純名は緩く再び時に役に立つかもしれない、クラスを識別するtoString操作や伐採が、一意であることが保証されていません。
  • タイプ名を返す「このタイプの名前のための有益な文字列」、「それは)のtoString(のようなものです:それは純粋に有益だと何の契約価値を持っていない」(sir4ur0nによって書かれたもの)

5
あなたは何が必要だと思いますか?
Nick Holt

2
@AnupamSainiはい。実際のアプリケーションでこのようなパッケージ名を使用するのは、おかしいでしょう。
Jayen 2014

3
ITはクレイジーですが、それは悪意のある俳優が働くことを可能にする種類の仮定です。「ああ、クラスが小文字で始まることは決してないことを知っている/パッケージは大文字で始まることは決してない」。確かに、クラスローダーにアクセスできる悪意のあるアクターはすでにひどいことをすることができるので、恐らく絶対にひどい仮定ではありません。
corsiKa 2014

2
@PieterDeBieどのように?知っておくべきことは、テストしたいメソッド名だけです。
fool4jesus

20
Java 8はgetTypeName()も追加しました...そのために更新しますか?
セオドアマードック

90

ローカルクラス、ラムダ、および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

  1. クラスオブジェクトがプリミティブ型またはを表す場合、void4つのメソッドはすべて単にその名前を返します。

次に、getName()メソッドのルール:

  1. すべての非ラムダ配列および非配列クラスまたはインターフェース(つまり、トップレベル、ネストされた、内部、ローカル、匿名)にgetName()は、パッケージ名の後にドットが続く名前(によって返される)があります(パッケージがある場合) )、コンパイラによって生成されたクラスファイルの名前が続きます(接尾辞は除く.class)。パッケージがない場合は、単にクラスファイルの名前です。クラスが内部クラス、ネストされたクラス、ローカルクラス、または匿名クラスの場合、コンパイラは$クラスファイル名に少なくとも1つを生成する必要があります。匿名クラスの場合、クラス名はドル記号の後に数字が続くことに注意してください。
  2. Lambdaクラス名は一般に予測不可能であり、とにかくそれらについて気にする必要はありません。正確には、それらの名前は、囲んでいるクラスの名前の後に、、$$Lambda$数字、スラッシュ、別の数字が続きます。
  3. プリミティブのクラス記述子は、Zfor booleanBfor byteSfor shortCfor charIfor intJfor longFfor floatDfor doubleです。非配列クラスおよびインタフェースのクラス記述は、Lで与えられるものが続くgetName()続きます;。配列クラスの場合、クラス記述子の[後にコンポーネントタイプのクラス記述子が続きます(それ自体が別の配列クラスの場合があります)。
  4. 配列クラスの場合、getName()メソッドはそのクラス記述子を返します。このルールは、コンポーネントタイプがラムダ(バグである可能性があります)である配列クラスでのみ失敗するようですが、コンポーネントタイプがラムダである配列クラスの存在にも意味がないため、これは問題にならないはずです。

さて、toString()メソッド:

  1. クラスインスタンスがインターフェース(または特別なタイプのインターフェースであるアノテーション)を表す場合、はをtoString()返します"interface " + getName()。プリミティブの場合は、単にを返しますgetName()。それが他のものである場合(かなり奇妙なものであっても、クラス型)、を返します"class " + getName()

getCanonicalName()方法:

  1. トップレベルのクラスとインターフェースの場合、getCanonicalName()メソッドはメソッドが返すものだけをgetName()返します。
  2. このgetCanonicalName()メソッドはnull、匿名クラスまたはローカルクラス、およびそれらの配列クラスに戻ります。
  3. 内部クラスおよびネストされたクラスとインターフェースの場合、このgetCanonicalName()メソッドはgetName()、コンパイラーが導入したドル記号をドットで置き換えたものを返します。
  4. 配列クラスの場合、getCanonicalName()メソッドはnull、コンポーネントタイプの正規名がである場合に戻りますnull。それ以外の場合は、コンポーネントタイプの正規名とそれに続くを返します[]

getSimpleName()方法:

  1. トップレベルのネストされた内部クラスとローカルクラスの場合、getSimpleName()は、ソースファイルに記述されているクラスの名前を返します。
  2. 匿名クラスの場合getSimpleName()、空のを返しますString
  3. ラムダクラスの場合、はパッケージ名なしでがgetSimpleName()返すものを返すだけgetName()です。これはあまり意味がなく、バグのように見えますが、getSimpleName()最初にラムダクラスを呼び出す意味はありません。
  4. 配列クラスの場合、このgetSimpleName()メソッドは、コンポーネントクラスの単純な名前に続いてを返します[]。これには、コンポーネント型が匿名クラスである配列クラスが[]単純な名前と同じであるという奇妙な奇妙な副作用があります。

2
… replacing the dollar-signs by dots:区切り文字として導入されたドル記号のみが置き換えられます。単純な名前の一部としてドルを使用することもでき、それらはそのまま残ります。
MvG

大野!クラス名の一部として!私はクラスの変圧器を開発していると私は「/」クラスとパッケージ名の間に安全な区切りであろうと考えた:/
ホセ・ロベルト・アラウージョジュニオール

81

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[]

28
上記の回答の編集を提案する方がはるかに良いでしょう。
LoKi 2015年

17

私はさまざまな命名方式にも戸惑っていましたが、ここでこの質問を見つけたとき、これについて自分の質問をして答えようとしていました。私の調査結果はそれに十分に適合し、すでにここにあるものを補完すると思います。私の焦点は、さまざまな用語に関するドキュメントを探し、他の場所で出現する可能性のあるいくつかの関連用語を追加することです。

次の例を考えてみます。

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$DD[La.b.C$D;D[]

  • この答えは、同じクラスローダーによってロードされた2つのクラスが、同じ正規名を持つが異なるバイナリ名を持つことが可能であることを示しています。どちらの名前も、もう一方を確実に推測するには不十分です。正規名がある場合、名前のどの部分がパッケージで、どの部分がクラスを含んでいるかがわかりません。バイナリ名を持っている場合$、セパレータとして導入されたものと、単純な名前の一部だったものがわかりません。(クラスファイルには、クラス自体とそれを含むクラスのバイナリ名格納されます。これにより、ランタイムがこの区別行うことができます。)

  • 匿名クラスローカルクラスに完全修飾名はありませんが、バイナリ名があります。同じことが、そのようなクラス内にネストされたクラスにも当てはまります。すべてのクラスにはバイナリ名があります。

  • 実行javap -v -private上のa/b/C.classバイトコードがのタイプを指すことを示しているdとしてLa/b/C$D;、アレイのことdsとして[La/b/C$D;。これらは記述子と呼ばれ、JVMSセクション4.3で指定されています。

  • a/b/C$Dこれらの記述子の両方で使用されるクラス名は、バイナリ名で置き換えること.によって得られるものです/。JVM仕様では、これをバイナリ名の内部形式と呼んでいます。JVMSセクション4.2.1でそれを説明し、バイナリ名との違いは歴史的な理由によるものであると述べています。

  • 典型的なファイル名ベースのクラスローダーの1つにあるクラスのファイル名/、バイナリ形式の内部形式のをディレクトリセパレーターとして解釈し、それにファイル名拡張子.classを追加した場合に得られるものです。問題のクラスローダーが使用するクラスパスに関連して解決されます。


3
これはJLSを参照し、適切な用語を使用する唯一の回答であるため、受け入れられた回答になるはずです。
John

10

これは、getName()、getSimpleName()、getCanonicalName()について説明している中で見つけた最高のドキュメントです。

https://javahowtodoit.wordpress.com/2014/09/09/java-lang-class-what-is-the-difference-between-class-getname-class-getcanonicalname-and-class-getsimplename/

// 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[]

3

興味深いことに、クラス名が不正な形式であるgetCanonicalName()と発生getSimpleName()する可能性がありますInternalError。これは、Scalaなどの一部の非Java JVM言語で発生します。

以下を検討してください(Java 8上のScala 2.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

これは、混合言語環境や、アプリサーバーやその他のプラットフォームソフトウェアなど、バイトコードを動的にロードする環境では問題になる可能性があります。


2

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つの違いは、匿名クラスを使用している場合、クラスの名前をgetCanonicalName()

もう1つの事実は、getName()メソッドの動作getCanonicalName()内部クラスのメソッドとは異なることです。getName()囲んでいるクラスの正規名と内部クラスの単純名の間の区切り文字としてドルを使用します。

Javaでクラス名を取得する方法の詳細。


1
    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

1
メソッド内の最初の2行は次のように削減できますClass<StringBuffer> clazz = StringBuffer.class
ThePyroEagle
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.