Javaの静的メソッドからクラス名を取得する


244

そのクラスの静的メソッドからクラスの名前を取得するにはどうすればよいですか。例えば

public class MyClass {
    public static String getClassName() {
        String name = ????; // what goes here so the string "MyClass" is returned
        return name;
    }
}

コンテキストで説明するために、実際には、例外のメッセージの一部としてクラス名を返したいと思います。


try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();}
arminvanbuuren 2018

回答:


231

リファクタリングを正しくサポートするには(クラスの名前を変更)、次のいずれかを使用する必要があります。

 MyClass.class.getName(); // full name with package

または(@James Van Huisに感謝):

 MyClass.class.getSimpleName(); // class name and no more

136
そのようにMyClassの知識をハードコードする場合は、String name = "MyClass";を実行することもできます。!
John Topley、

111
ただし、IDEでクラス名をリファクタリングすると正しく機能しません。
James Van Huis、

9
そうだね。MyClass.classは、この行が「クラス名の変更」リファクタリングで忘れられないことを保証します
ツールキット

19
私が望む「これは」という「class.xxxが」このクラスを意味するように、インスタンスまたは静的コードのいずれかで許可されたことが、Javaで現在のクラスを意味するように、静的コンテキストで働いていました!これに関する問題は、コンテキスト内でMyClassが冗長で冗長であることです。しかし、私がJavaを好きなだけで、それは冗長に傾いているようです。
Lawrence Dol

51
サブクラスで静的メソッドを呼び出していて、サブクラス名が必要な場合はどうなりますか?
Edward Falk

120

ツールキットが言うことを行います。次のようなことはしないでください。

return new Object() { }.getClass().getEnclosingClass();

7
クラスが別のクラスを拡張する場合、これは実際のクラスを返さず、基本クラスのみを返します。
Luis Soeiro 2012

1
@LuisSoeiroメソッドが定義されているクラスを返すと思います。基本クラスが静的コンテキストにどのように影響するかはわかりません。
トム・ホーティン-タックライン2012

8
getClass()を静的にできない理由がわかりません。その場合、この「イディオム」は必要ありません。
mmirwaldt 2013

1
@mmirwaldt getClassはランタイム型を返すため、静的にすることはできません。
トム・ホーティン-タックライン2013年

5
これが本当の答えです。自分でクラスの名前をコードに書く必要がないからです。
Bobs

99

Java 7以降では、静的メソッド/フィールドでこれを行うことができます。

MethodHandles.lookup().lookupClass()

私は言うつもりだったReflection.getCallerClass()。しかし、それは「sun」パッケージに入っていることについて警告を与えます。したがって、これはより良い解決策かもしれません。
Foumpie

1
@Foumpie:Java 9は、この非公式のReflection.getCallerClass()ものに取って代わる公式APIを導入する予定です。それは彼のささいな操作のために少し複雑です、すなわちOptional<Class<?>> myself = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass) .findFirst());、もちろん、それはそれがはるかに強力になるという事実に関連しています。
ホルガー

6
これは簡単に最適なソリューションです。実際のクラス名を指定する必要がなく、あいまいではなく、ハッキングではありません。ArtyomKrivolapovの以下の投稿によると、これも非常に高速なアプローチです。
skomisa 2018年

@Rein基本クラスで呼び出された場合、ランタイムクラスを取得する方法はありますか?
Glide

59

したがって、MyClass.class構文を明示的に使用せずに、クラスオブジェクトまたはクラスのフル/シンプル名を静的に取得する必要がある場合があります。

いくつかのケースでは本当に便利です。たとえば、 上位レベルの関数(この場合、kotlinは、kotlinコードからアクセスできない静的Javaクラスを作成します)。

この情報を取得するためのいくつかの異なるバリアントがあります。

  1. new Object(){}.getClass().getEnclosingClass();
    トム・ホーティンが 指摘-タックライン

  2. getClassContext()[0].getName();からSecurityManager
    で指摘クリストファー

  3. new Throwable().getStackTrace()[0].getClassName();
    カウントルートヴィヒ

  4. Thread.currentThread().getStackTrace()[1].getClassName();
    ケクシ から

  5. そして最後にレイン
    MethodHandles.lookup().lookupClass();
    から素晴らしい


私は準備しました すべてのバリアントと結果のベンチマークは次のとおりです。

# Run complete. Total time: 00:04:18

Benchmark                                                      Mode  Cnt      Score     Error  Units
StaticClassLookup.MethodHandles_lookup_lookupClass             avgt   30      3.630 ±   0.024  ns/op
StaticClassLookup.AnonymousObject_getClass_enclosingClass      avgt   30    282.486 ±   1.980  ns/op
StaticClassLookup.SecurityManager_classContext_1               avgt   30    680.385 ±  21.665  ns/op
StaticClassLookup.Thread_currentThread_stackTrace_1_className  avgt   30  11179.460 ± 286.293  ns/op
StaticClassLookup.Throwable_stackTrace_0_className             avgt   30  10221.209 ± 176.847  ns/op


結論

  1. 使用するの最適なバリアント。かなりクリーンで、驚くほど高速です。
    Java 7およびAndroid API 26以降でのみ使用可能です。
 MethodHandles.lookup().lookupClass();
  1. AndroidまたはJava 6でこの機能が必要な場合は、2番目に優れたバリアントを使用できます。これもかなり高速ですが、使用場所ごとに匿名クラスを作成します :(
 new Object(){}.getClass().getEnclosingClass();
  1. 多くの場所でそれが必要であり、大量の匿名クラスのためにバイトコードが肥大化したくない場合SecurityManagerは、あなたの友人です(3番目に最適なオプション)。

    しかし、あなたはただ呼び出すことはできませんgetClassContext()–それはSecurityManagerクラスで保護されています。次のようなヘルパークラスが必要になります。

 // Helper class
 public final class CallerClassGetter extends SecurityManager
 {
    private static final CallerClassGetter INSTANCE = new CallerClassGetter();
    private CallerClassGetter() {}

    public static Class<?> getCallerClass() {
        return INSTANCE.getClassContext()[1];
    }
 }

 // Usage example:
 class FooBar
 {
    static final Logger LOGGER = LoggerFactory.getLogger(CallerClassGetter.getCallerClass())
 }
  1. getStackTrace()from例外またはに基づく最後の2つのバリアントを使用する必要はおそらくないでしょうThread.currentThread()非常に非効率的でStringClass<*>インスタンスではなくクラス名のみをとして返す可能性があります。


PS

静的kotlin utils(私のように)のロガーインスタンスを作成する場合は、次のヘルパーを使用できます。

import org.slf4j.Logger
import org.slf4j.LoggerFactory

// Should be inlined to get an actual class instead of the one where this helper declared
// Will work only since Java 7 and Android API 26!
@Suppress("NOTHING_TO_INLINE")
inline fun loggerFactoryStatic(): Logger
    = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())

使用例:

private val LOGGER = loggerFactoryStatic()

/**
 * Returns a pseudo-random, uniformly distributed value between the
 * given least value (inclusive) and bound (exclusive).
 *
 * @param min the least value returned
 * @param max the upper bound (exclusive)
 *
 * @return the next value
 * @throws IllegalArgumentException if least greater than or equal to bound
 * @see java.util.concurrent.ThreadLocalRandom.nextDouble(double, double)
 */
fun Random.nextDouble(min: Double = .0, max: Double = 1.0): Double {
    if (min >= max) {
        if (min == max) return max
        LOGGER.warn("nextDouble: min $min > max $max")
        return min
    }
    return nextDouble() * (max - min) + min
}

38

この指示はうまくいきます:

Thread.currentThread().getStackTrace()[1].getClassName();

5
本当に遅いので注意してください。ただし、コピーして貼り付けることはできます。
ガーボルLipták

1
これには、オブジェクトまたはスレッドを使用するたびに作成する必要がないという利点があります。
Erel Segal-Halevi 2013

5
@ErelSegalHaleviそれでも、バックグラウンドでStackTraceElementの多くを作成します:(
Navin

4
のソースコードをThread.getStackTrace()見るとreturn (new Exception()).getStackTrace();、で呼び出された場合以外は何も実行されないことがわかりますcurrentThread()。したがって、@ countルートヴィヒの解決策は、それを実現するためのより直接的な方法です。
T-Bull

34

次のようなJNIを使​​用することで、本当にすばらしいことができます。

MyObject.java:

public class MyObject
{
    static
    {
        System.loadLibrary( "classname" );
    }

    public static native String getClassName();

    public static void main( String[] args )
    {
        System.out.println( getClassName() );
    }
}

次に:

javac MyObject.java
javah -jni MyObject

次に:

MyObject.c:

#include "MyObject.h"

JNIEXPORT jstring JNICALL Java_MyObject_getClassName( JNIEnv *env, jclass cls )
{
    jclass javaLangClass = (*env)->FindClass( env, "java/lang/Class" );
    jmethodID getName = (*env)->GetMethodID( env, javaLangClass, "getName",
        "()Ljava/lang/String;" );
    return (*env)->CallObjectMethod( env, cls, getName );
}

次に、Cをコンパイルして呼び出された共有ライブラリに入れlibclassname.so、Javaを実行します。

*含み笑い


なぜこれが組み込まれていないのですか?
ggb667 2014

賢い、そして私はユーモアに感謝します。軟膏のフライは、C関数のデフォルト名にJava_MyObject_getClassName名前が埋め込まれていることです。これを回避するには、JNIを使​​用しRegisterNativesます。もちろん、JNI FindClass(env, 'com/example/MyObject')でそれをフィードする必要があるので、そこでも勝てません。
2014年

2
@Renate、この全体の答えは実際には冗談ですか?もしそうなら、私たちが他の人々を助けることになっているので、それを非常に明確にしてください。
ステフェイン・グーリッホン

まあ、それは私のジョークではなく、それはジョークだと指摘しました。このシナリオのユースケースは通常、ログでクラスを識別するためのものです。すべての複雑さを離れてストリップ、それは通常どちらかやっに沸く:private static final String TAG = "MyClass"またはprivate static final String TAG = MyClass.class.getSimpleName();第二には、IDEを使用して名前を変更するグローバルクラスのために、より親しみやすいです。
2016年

20

これを使用して、クラスの上部にあるLog4j Loggerを初期化(または注釈)します。

PRO:Throwableは既にロードされており、「IO負荷の高い」SecurityManagerを使用しないことでリソースを節約できます。

CON:これがすべてのJVMで機能するかどうかに関する質問。

// Log4j . Logger --- Get class name in static context by creating an anonymous Throwable and 
// getting the top of its stack-trace. 
// NOTE you must use: getClassName() because getClass() just returns StackTraceElement.class 
static final Logger logger = Logger.getLogger(new Throwable() .getStackTrace()[0].getClassName()); 

jvmが邪魔にならないように独自の例外クラスを作成します。jhocr.googlecode.com
svn

1
このソリューションと同じくらいひどいものを提案する場合は、少なくともSecurityManagerの悪用などの恐ろしいソリューションではなく、プロをMyClass.class.getName()を使用する自然なソリューションに関連付けてください。
セーレンBoisen

その他の短所:冗長すぎます。遅い(クラスがロードされたときに一度だけ実行されるため、これは実際にはマイナーなポイントです)。
toolforger 2017年

13

SecurityManagerを悪用する

System.getSecurityManager().getClassContext()[0].getName();

または、設定されていない場合は、それを拡張する内部クラスを使用します(以下の例は、RealのHowToから恥ずかしくコピーされています)。

public static class CurrentClassGetter extends SecurityManager {
    public String getClassName() {
        return getClassContext()[1].getName(); 
    }
}

9

完全なパッケージ名が必要な場合は、次を呼び出します。

String name = MyClass.class.getCanonicalName();

最後の要素だけが必要な場合は、次を呼び出します。

String name = MyClass.class.getSimpleName();

5

呼び出し元のクラスをそのまま使用すると、MyClass.class.getName()実際に機能しますが、このクラス名が必要な多数のクラス/サブクラスにこのコードを伝播すると、コピー/貼り付けエラーが発生しやすくなります。

そしてトム・ホーティンのレシピは実際には悪くありません、ちょうどそれを正しい方法で調理する必要があります:)

サブクラスから呼び出すことができる静的メソッドを持つ基本クラスがあり、この静的メソッドが実際の呼び出し元のクラスを知る必要がある場合、これは次のようにして実現できます。

class BaseClass {
  static sharedStaticMethod (String callerClassName, Object... otherArgs) {
    useCallerClassNameAsYouWish (callerClassName);
    // and direct use of 'new Object() { }.getClass().getEnclosingClass().getName()'
    // instead of 'callerClassName' is not going to help here,
    // as it returns "BaseClass"
  }
}

class SubClass1 extends BaseClass {
  static someSubclassStaticMethod () {
    // this call of the shared method is prone to copy/paste errors
    sharedStaticMethod (SubClass1.class.getName(),
                        other_arguments);
    // and this call is safe to copy/paste
    sharedStaticMethod (new Object() { }.getClass().getEnclosingClass().getName(),
                        other_arguments);
  }
}

4

以下のアドホッククラスの定義を回避する、リファクタリングセーフ、カット&ペーストセーフソリューション。

メソッド名にクラス名が含まれるように注意しながら、クラス名を回復する静的メソッドを記述します。

private static String getMyClassName(){
  return MyClass.class.getName();
}

次に、それを静的メソッドで呼び出します。

public static void myMethod(){
  Tracer.debug(getMyClassName(), "message");
}

文字列の使用を回避することにより、安全なリファクタリングが提供されます。呼び出し元のメソッドを切り取って貼り付けた場合、ターゲットの「MyClass2」クラスにgetMyClassName()が見つからないため、再定義して更新する必要があります。


3

質問以来`ClassName.class`の代わりに` this.class`のような何か?(この質問はクラス名ではなくクラスに関するものなので、これは議論の余地があります)、私はここに答えを投稿しています:

class MyService {
    private static Class thisClass = MyService.class;
    // or:
    //private static Class thisClass = new Object() { }.getClass().getEnclosingClass();
    ...
    static void startService(Context context) {
        Intent i = new Intent(context, thisClass);
        context.startService(i);
    }
}

プライベートthisClassとして定義することが重要です 。1)継承してはなりません。派生クラスは独自に定義するか、エラーメッセージを生成する必要があります 。2)他のクラスからの参照は、ではなくとして行う必要があります。
thisClass
ClassName.classClassName.thisClass

thisClass定義され、クラス名へのアクセスは次のようになります。

thisClass.getName()

1

複数のクラスの静的メソッドにクラス名が必要だったので、次のメソッドでJavaUtilクラスを実装しました。

public static String getClassName() {
    String className = Thread.currentThread().getStackTrace()[2].getClassName();
    int lastIndex = className.lastIndexOf('.');
    return className.substring(lastIndex + 1);
}

それが役に立てば幸い!


1
マジックナンバー2(NullPointerExceptionが発生しやすい)のためにこれを使用するのは悪いだけでなく、仮想マシンの精度に大きく依存しています。メソッドのjavadocから:*一部の仮想マシンは、特定の状況下で、スタックトレースから1つ以上のスタックフレームを省略することがあります。極端な場合、このスレッドに関するスタックトレース情報を持たない仮想マシンは、このメソッドから長さ0の配列を返すことが許可されます。*
Shotgun 2015年

0

私は両方にこれら2つのアプローチを使用しました staticnon staticのシナリオ:

メインクラス:

//For non static approach
public AndroidLogger(Object classObject) {
    mClassName = classObject.getClass().getSimpleName();
}

//For static approach
public AndroidLogger(String className) {
    mClassName = className;
}

クラス名を提供する方法:

非静的な方法:

private AndroidLogger mLogger = new AndroidLogger(this);

静的な方法:

private static AndroidLogger mLogger = new AndroidLogger(Myclass.class.getSimpleName());

-1

リフレクションを使用している場合は、Methodオブジェクトを取得して、次のことができます。

method.getDeclaringClass().getName()

メソッド自体を取得するには、おそらく以下を使用できます。

Class<?> c = Class.forName("class name");
Method  method = c.getDeclaredMethod ("method name", parameterTypes)

3
そして、どのようにして「クラス名」を知るのでしょうか?:)
alfasin 2016

1
Class.forName("class name")、すでにあなたのクラスを与えます。メソッドを介してそれを取得したいのはなぜですか?
Sergey Irisov 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.