HOWTO · Java

Java でクラス名を取得する

この記事では、Java でクラス名を取得する方法を示します

このページの内容

Java のクラス名を取得するときは、まず必要な名前の形式を選びます。オブジェクトに getClass() を呼び出すと、実行時の Class<?> オブジェクトを取得できます。そのクラスに対して getSimpleName()、getName()、または getCanonicalName() を呼び出します。型がすでにわかっている場合は、String.class のようなクラスリテラルを使います。3つの名前取得メソッドは、ネストしたクラスや特殊なクラスでは結果が異なるため、互いに置き換えて使うことはできません。

必要なクラス名の形式を選ぶ

簡潔な表示名には getSimpleName()、JVM のバイナリ名には getName()、存在する場合のソースコード形式の正規名には getCanonicalName() を使います。単純名にはパッケージ名が含まれません。バイナリ名にはパッケージ名の接頭辞や、ネストした型を区切る $ が含まれることがあります。正規名では外側の型とネストした型をドットで区切ります。ただし、すべてのクラスに正規名があるわけではなく、null になることもあります。

これらの値は名前であり、Class オブジェクトではありません。表示、診断、リフレクション関連の調査には役立ちますが、生成された名前を永続化や外部プロトコルで使う安定した識別子にしないでください。コンパイル時に型がわかっている場合は、クラスリテラルを使えば、先にインスタンスを調べる必要はありません。

getClass() でオブジェクトの実行時クラスを取得する

getClass() は Object から継承されるインスタンスメソッドです。オブジェクトの実際の実行時クラスを Class<?> として返し、文字列は返しません。参照先のオブジェクトよりも変数の宣言型のほうが広い場合に便利です。たとえば、Object 型として宣言された変数からでも、現在の値の実行時クラスを調べられます。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

public class ClassNameExample {
    public static void main(String[] args) {
        Object value = "hello";
        Class<?> runtimeClass = value.getClass();

        System.out.println(runtimeClass.getSimpleName());
        System.out.println(runtimeClass.getName());
    }
}
String
java.lang.String

1行目は読みやすい単純名を、2行目はバイナリ名を取得しています。この例では、変数 value の宣言型である Object ではなく、値の実行時クラスである String を使っています。型がすでにわかっている場合は、String.class を使うと、文字列のインスタンスを作らずに Class<String> オブジェクトを取得できます。

getSimpleName()、getName()、getCanonicalName() を使い分ける

各名前取得メソッドは Class<?> オブジェクトに対して呼び出します。java.util.ArrayList のようなトップレベルクラスでは、getSimpleName() は ArrayList を返し、getName() と getCanonicalName() は java.util.ArrayList を返します。メンバークラス Outer.Inner の単純名は Inner です。バイナリ名では2つのクラスの間を $ で区切り、正規名ではドットで区切ります。

メソッド 用途 典型的な形式
getSimpleName() 簡潔な表示 ArrayList
getName() JVM のバイナリ名 java.util.ArrayList
getCanonicalName() 利用可能な場合のソースコード形式の名前 java.util.ArrayList

クラスが名前付きパッケージに属する場合は、パッケージ名の接頭辞が付きます。デフォルトパッケージのクラスにはパッケージ名が付きません。getName() は、常に完全修飾されたソースコード上の名前になるわけではありません。配列クラスでは JVM 形式の記述子が使われ、ローカルクラスや匿名クラスでは生成されたバイナリ名が使われます。ローカルクラスや匿名クラスに対する getCanonicalName() は null を返すことがあります。常に完全修飾名が得られると考えず、その文字列を使う側の要件に応じてメソッドを選んでください。

クラスリテラルにも同じメソッドを使えます。たとえば、既知の型に対して String.class.getSimpleName() を呼び出すと、その単純名が返ります。型が実行時にならないとわからないオブジェクトでは、先に value.getClass() を使います。

匿名クラスなどの特殊なクラスを扱う

匿名クラスにはソースコード上の名前がありません。JVM は匿名クラスに生成されたバイナリ名を割り当てます。名前には $ と数字が含まれることがよくありますが、正確な表記はコンパイラによって異なるため、安定した名前として扱ってはいけません。匿名クラスに対して getSimpleName() は空文字列を返し、getCanonicalName() は null を返します。getName() は生成されたバイナリ名を返します。

ローカルクラスはメソッドまたは初期化子の内部で宣言されます。ローカルクラスにも生成されたバイナリ名があり、正規名は null です。一方、メンバークラスは別のクラスのメンバーとして宣言され、ソースコード上の単純名と正規名を持ちます。この違いにより、getClass() が実行時クラスを正しく識別していても、名前取得メソッドが空文字列や null を返すことがあります。

匿名クラスやローカルクラスの実装に信頼できるラベルが必要なら、アプリケーション内でそのラベルを明示的に定義してください。生成された名前を解析したり、そこから利用者向けの型名を推測したりしないでください。

スタックトレースから呼び出し元のクラスを取得する

特定のフレームを識別する必要がある場合はスタックトレースを調べられますが、対象のオブジェクトに getClass() を呼び出す方法より直接的ではありません。Thread.currentThread().getStackTrace() は StackTraceElement の配列を返します。各要素の getClassName() は、そのフレームに記録されたクラス名を返します。次の構文はフレームを選択する方法を示しますが、インデックスは呼び出し経路や実行環境によって異なるため、任意の呼び出し元を取得できる移植性のある方法ではありません。

Thread.currentThread().getStackTrace()[2].getClassName()

現在のメソッドとスタックトレースを取得するメソッドもフレームを占めるため、単純な呼び出し経路ではインデックス 2 が直近の呼び出し元を指すことがよくあります。ラッパーメソッド、仮想マシン、実行時の最適化によって、存在するフレームは変わることがあります。診断目的でこの方法を使う場合は、対象のプログラムでフレーム一覧を確認してください。通常のクラス名取得ではスタックをたどる方法を避け、呼び出し元の識別情報が API の契約に含まれる場合は、オブジェクトやクラスの情報を直接渡す方法を優先してください。