1103

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

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

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

4

8 に答える 8

1253

何かわからない場合は、最初にテストを書いてみてください。

これは私がしました:

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言語仕様のドキュメントを参照できます。

Example 6.7-2.とそれぞれExample 6.7-2.行きますFully Qualified NamesFully Qualified Names v. Canonical Name

于 2013-03-04T14:07:27.310 に答える
112

ローカルクラス、ラムダ、および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$、その後に、数字、スラッシュ、別の数字が続きます。
  3. プリミティブのクラス記述子は、Zfor booleanBfor byteSfor shortCfor charIfor intJfor longFfor 、floatおよびDfordoubleです。非配列クラスおよびインターフェースの場合、クラス記述子のL後には、で指定されたものが続き、その後に。がgetName()続き;ます。配列クラスの場合、クラス記述子[の後にコンポーネントタイプのクラス記述子が続きます(それ自体が別の配列クラスである場合があります)。
  4. 配列クラスの場合、getName()メソッドはそのクラス記述子を返します。このルールは、コンポーネントタイプがラムダである配列クラス(バグである可能性があります)に対してのみ失敗するようですが、コンポーネントタイプがラムダである配列クラスが存在しても意味がないため、これが問題にならないことを願っています。

さて、toString()メソッド:

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

getCanonicalName()方法:

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

getSimpleName()方法:

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

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[]
于 2014-05-08T15:06:45.987 に答える
21

さまざまな命名スキームにも混乱していて、ここでこの質問を見つけたとき、これについて自分自身の質問をして答えようとしていました。私の調査結果はそれに十分に適合し、すでにここにあるものを補完すると思います。私の焦点は、さまざまな用語に関するドキュメントを探し、他の場所で発生する可能性のある関連用語を追加することです。

次の例を考えてみましょう。

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.Da.b.C.D.D.Dは両方とも完全修飾名ですが、の正規名のみにa.b.C.Dなります。したがって、すべての正規名は完全修飾名ですが、その逆は常に正しいとは限りません。正規名またはを返します。DClass.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と、バイトコードが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を追加すると得られるものです。問題のクラスローダーによって使用されるクラスパスに関連して解決されます。

于 2016-05-02T09:15:39.977 に答える
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[]
于 2016-06-02T23:12:25.070 に答える
5

興味深いことに、クラス名が不正な場合に発生する可能性が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

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

于 2017-08-23T01:27:28.183 に答える
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つの違いは、匿名クラスを使用する場合、を使用してクラスの名前を取得しようとするとnull値を取得できることです。getCanonicalName()

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

Javaでのクラス名の取得について詳しく知るため。

于 2019-10-07T03:54:31.450 に答える
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
于 2016-10-09T14:15:30.300 に答える