web-dev-qa-db-ja.com

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

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

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

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

224
Miles D

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

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

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

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

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

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

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

MethodHandles.lookup().lookupClass()
76
Rein

そのため、MyClass.class構文を明示的に使用せずに、クラスオブジェクトまたはクラスの完全/単純な名前を静的に取得する必要がある場合があります。

場合によっては本当に便利です。 のロガーインスタンスkotlin 上位レベル関数(この場合、kotlinはkotlinコードからアクセスできない静的Javaクラスを作成します)。

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

  1. new Object(){}.getClass().getEnclosingClass();
    注記: Tom Hawtin-tackline

  2. SecurityManagerからのgetClassContext()[0].getName();
    注記: クリストファー

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

  4. Thread.currentThread().getStackTrace()[1].getClassName();
    from Keksi

  5. そして最後に素晴らしい
    MethodHandles.lookup().lookupClass();
    from 手綱


すべてのバリアントの jmh ベンチマークを用意しました。結果は次のとおりです。

# 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 AP​​I 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 exceptionまたはThread.currentThread()に基づく最後の2つのバリアントを使用する必要はおそらくないでしょう。 非常に非効率的であり、クラス名のみをClass<*>インスタンスではなくStringとして返すことができます。


追伸.

静的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
}
40

この指示は正常に機能します。

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

次のような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を実行します!

*含み笑い

34
lifelongcoug

これを使用して、クラスの上部で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()); 
21
count ludwig

SecurityManagerを悪用する

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

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

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

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

String name = MyClass.class.getCanonicalName();

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

String name = MyClass.class.getSimpleName();
9
James Van Huis

MyClass.class.getName()のような呼び出し側のクラスの逐語的な使用は実際に仕事をしますが、このコードをこのクラス名が必要な多数のクラス/サブクラスに伝搬する場合、エラーをコピー/貼り付けがちです。

そして Tom Hawtinのレシピ は実際悪くありません。正しい方法で調理するだけです:)

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

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);
  }
}
5
Sergey Ushakov

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

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

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

静的メソッドでそれを思い出してください:

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

リファクタリングの安全性は、文字列の使用を避けることによって与えられます。呼び出し元メソッドをカットアンドペーストすると、ターゲットの「MyClass2」クラスにgetMyClassName()が見つからないため、カットアンドペーストの安全性が与えられます。

4
avalori

質問 `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);
    }
}

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

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

thisClass.getName()

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

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

それが役立つことを願っています!

1
phaderer

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

メインクラス:

//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());
0
0xAliHn