回答:
リファクタリングを正しくサポートするには(クラスの名前を変更)、次のいずれかを使用する必要があります。
MyClass.class.getName(); // full name with package
または(@James Van Huisに感謝):
MyClass.class.getSimpleName(); // class name and no more
ツールキットが言うことを行います。次のようなことはしないでください。
return new Object() { }.getClass().getEnclosingClass();
getClass
はランタイム型を返すため、静的にすることはできません。
Java 7以降では、静的メソッド/フィールドでこれを行うことができます。
MethodHandles.lookup().lookupClass()
Reflection.getCallerClass()
。しかし、それは「sun」パッケージに入っていることについて警告を与えます。したがって、これはより良い解決策かもしれません。
Reflection.getCallerClass()
ものに取って代わる公式APIを導入する予定です。それは彼のささいな操作のために少し複雑です、すなわちOptional<Class<?>> myself = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass) .findFirst());
、もちろん、それはそれがはるかに強力になるという事実に関連しています。
したがって、MyClass.class
構文を明示的に使用せずに、クラスオブジェクトまたはクラスのフル/シンプル名を静的に取得する必要がある場合があります。
いくつかのケースでは本当に便利です。たとえば、 コトリン 上位レベルの関数(この場合、kotlinは、kotlinコードからアクセスできない静的Javaクラスを作成します)。
この情報を取得するためのいくつかの異なるバリアントがあります。
new Object(){}.getClass().getEnclosingClass();
トム・ホーティンが
指摘-タックライン
getClassContext()[0].getName();
からSecurityManager
で指摘クリストファー
new Throwable().getStackTrace()[0].getClassName();
カウントルートヴィヒ
Thread.currentThread().getStackTrace()[1].getClassName();
ケクシ
から
そして最後にレイン
MethodHandles.lookup().lookupClass();
から素晴らしい
私は準備しました 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
MethodHandles.lookup().lookupClass();
new Object(){}.getClass().getEnclosingClass();
多くの場所でそれが必要であり、大量の匿名クラスのためにバイトコードが肥大化したくない場合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())
}
getStackTrace()
from例外またはに基づく最後の2つのバリアントを使用する必要はおそらくないでしょうThread.currentThread()
。非常に非効率的でString
、Class<*>
インスタンスではなくクラス名のみをとして返す可能性があります。静的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
}
この指示はうまくいきます:
Thread.currentThread().getStackTrace()[1].getClassName();
Thread.getStackTrace()
見るとreturn (new Exception()).getStackTrace();
、で呼び出された場合以外は何も実行されないことがわかりますcurrentThread()
。したがって、@ countルートヴィヒの解決策は、それを実現するためのより直接的な方法です。
次のような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を実行します。
*含み笑い
Java_MyObject_getClassName
名前が埋め込まれていることです。これを回避するには、JNIを使用しRegisterNatives
ます。もちろん、JNI FindClass(env, 'com/example/MyObject')
でそれをフィードする必要があるので、そこでも勝てません。
private static final String TAG = "MyClass"
またはprivate static final String TAG = MyClass.class.getSimpleName();
第二には、IDEを使用して名前を変更するグローバルクラスのために、より親しみやすいです。
これを使用して、クラスの上部にある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());
SecurityManagerを悪用する
System.getSecurityManager().getClassContext()[0].getName();
または、設定されていない場合は、それを拡張する内部クラスを使用します(以下の例は、RealのHowToから恥ずかしくコピーされています)。
public static class CurrentClassGetter extends SecurityManager {
public String getClassName() {
return getClassContext()[1].getName();
}
}
完全なパッケージ名が必要な場合は、次を呼び出します。
String name = MyClass.class.getCanonicalName();
最後の要素だけが必要な場合は、次を呼び出します。
String name = MyClass.class.getSimpleName();
呼び出し元のクラスをそのまま使用すると、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);
}
}
以下のアドホッククラスの定義を回避する、リファクタリングセーフ、カット&ペーストセーフソリューション。
メソッド名にクラス名が含まれるように注意しながら、クラス名を回復する静的メソッドを記述します。
private static String getMyClassName(){
return MyClass.class.getName();
}
次に、それを静的メソッドで呼び出します。
public static void myMethod(){
Tracer.debug(getMyClassName(), "message");
}
文字列の使用を回避することにより、安全なリファクタリングが提供されます。呼び出し元のメソッドを切り取って貼り付けた場合、ターゲットの「MyClass2」クラスにgetMyClassName()が見つからないため、再定義して更新する必要があります。
質問以来`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.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);
}
それが役に立てば幸い!
私は両方にこれら2つのアプローチを使用しました static
とnon 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());
リフレクションを使用している場合は、Methodオブジェクトを取得して、次のことができます。
method.getDeclaringClass().getName()
メソッド自体を取得するには、おそらく以下を使用できます。
Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod ("method name", parameterTypes)
Class.forName("class name")
、すでにあなたのクラスを与えます。メソッドを介してそれを取得したいのはなぜですか?
try{ throw new RuntimeEsception();} catch(RuntimeEcxeption e){return e.getstackTrace()[1].getClassName();
}