Javaで現在実行中のメソッドの名前を取得する方法はありますか?
Thread.currentThread().getStackTrace()
には通常、呼び出し元のメソッドが含まれますが、落とし穴があります( Javadoc を参照)。
状況によっては、一部の仮想マシンでスタックトレースから1つ以上のスタックフレームが省略されることがあります。極端な場合、このスレッドに関するスタックトレース情報を持たない仮想マシンは、このメソッドから長さゼロの配列を返すことが許可されます。
技術的にはこれはうまくいくでしょう...
String name = new Object(){}.getClass().getEnclosingMethod().getName();
ただし、コンパイル時に新しい匿名内部クラスが作成されます(例:YourClass$1.class
)。それで、これはこのトリックを展開する各メソッドのための.class
ファイルを作成します。さらに、ランタイム中の呼び出しごとに、他の点では未使用のオブジェクトインスタンスが作成されます。そのため、これは許容可能なデバッグ方法ですが、かなりのオーバーヘッドがあります。
このトリックの利点は、getEncosingMethod()
がJava.lang.reflect.Method
を返すことです。これは、アノテーションやパラメータ名を含むメソッドの他のすべての情報を取得するために使用できます。これにより、同じ名前を持つ特定のメソッドを区別することができます(メソッドオーバーロード)。
getEnclosingMethod()
のJavaDocによると、内部クラスは同じクラスローダーを使ってロードされるべきであるので、このトリックはSecurityException
を投げるべきではありません。そのため、セキュリティマネージャが存在していてもアクセス状況を確認する必要はありません。
コンストラクタにはgetEnclosingConstructor()
を使う必要があります。 (名前付き)メソッドの外側のブロックの間、getEnclosingMethod()
はnull
を返します。
2009年1月
完全なコードは次のようになります( @ Bombeの警告 を念頭に置いて使用します)。
/**
* Get the method name for a depth in call stack. <br />
* Utility function
* @param depth depth in the call stack (0 means current method, 1 means call method, ...)
* @return method name
*/
public static String getMethodName(final int depth)
{
final StackTraceElement[] ste = Thread.currentThread().getStackTrace();
//System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
// return ste[ste.length - depth].getMethodName(); //Wrong, fails for depth = 0
return ste[ste.length - 1 - depth].getMethodName(); //Thank you Tom Tresansky
}
もっと この質問 。
2011年12月に更新:
青みがかった コメント:
JRE 6を使用していますが、間違ったメソッド名を使用しています。
ste[2 + depth].getMethodName().
と書くとうまくいきます
0
はgetStackTrace()
です。1
はgetMethodName(int depth)
であり、2
はmethodを呼び出しています。
virgo47 's answer (upvoted)は実際にはメソッド名を返すために適用する正しいインデックスを計算します。
このコードを使用して、スタックトレースインデックスの潜在的な変動性を軽減しました - 現在はmethodName utilを呼び出すだけです。
public class MethodNameTest {
private static final int CLIENT_CODE_STACK_INDEX;
static {
// Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
int i = 0;
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
i++;
if (ste.getClassName().equals(MethodNameTest.class.getName())) {
break;
}
}
CLIENT_CODE_STACK_INDEX = i;
}
public static void main(String[] args) {
System.out.println("methodName() = " + methodName());
System.out.println("CLIENT_CODE_STACK_INDEX = " + CLIENT_CODE_STACK_INDEX);
}
public static String methodName() {
return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX].getMethodName();
}
}
過剰に設計されているように見えますが、JDK 1.5には一定の数があり、JDK 1.6に移行したときにそれが変更されたことに少し驚きました。これはJava 6/7でも同じですが、あなたは決して知りません。ランタイム中にそのインデックスが変更されたことを証明するものではありません - しかしHotSpotがそれを悪くしないことを願っています。 :-)
public class SomeClass {
public void foo(){
class Local {};
String name = Local.class.getEnclosingMethod().getName();
}
}
nameは値fooを持ちます。
最速の方法 私は見つけた:
import Java.lang.reflect.Method;
public class TraceHelper {
// save it static to have it available on every call
private static Method m;
static {
try {
m = Throwable.class.getDeclaredMethod("getStackTraceElement",
int.class);
m.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getMethodName(final int depth) {
try {
StackTraceElement element = (StackTraceElement) m.invoke(
new Throwable(), depth + 1);
return element.getMethodName();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
ネイティブメソッドgetStackTraceElement(int depth)に直接アクセスします。そして、アクセス可能なMethodを静的変数に格納します。
これらのオプションは両方とも、私にはJavaで機能します。
new Object(){}.getClass().getEnclosingMethod().getName()
または
Thread.currentThread().getStackTrace()[1].getMethodName()
次のコードを使用してください。
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
String methodName = e.getMethodName();
System.out.println(methodName);
public static String getCurrentMethodName() {
return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
}
これは virgo47の答え (上記)を拡張したものです。
これは、現在および呼び出し元のクラス/メソッド名を取得するためのいくつかの静的メソッドを提供します。
/* Utility class: Getting the name of the current executing method
* https://stackoverflow.com/questions/442747/getting-the-name-of-the-current-executing-method
*
* Provides:
*
* getCurrentClassName()
* getCurrentMethodName()
* getCurrentFileName()
*
* getInvokingClassName()
* getInvokingMethodName()
* getInvokingFileName()
*
* Nb. Using StackTrace's to get this info is expensive. There are more optimised ways to obtain
* method names. See other stackoverflow posts eg. https://stackoverflow.com/questions/421280/in-Java-how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection/2924426#2924426
*
* 29/09/2012 (lem) - added methods to return (1) fully qualified names and (2) invoking class/method names
*/
package com.stackoverflow.util;
public class StackTraceInfo
{
/* (Lifted from virgo47's stackoverflow answer) */
private static final int CLIENT_CODE_STACK_INDEX;
static {
// Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
int i = 0;
for (StackTraceElement ste: Thread.currentThread().getStackTrace())
{
i++;
if (ste.getClassName().equals(StackTraceInfo.class.getName()))
{
break;
}
}
CLIENT_CODE_STACK_INDEX = i;
}
public static String getCurrentMethodName()
{
return getCurrentMethodName(1); // making additional overloaded method call requires +1 offset
}
private static String getCurrentMethodName(int offset)
{
return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getMethodName();
}
public static String getCurrentClassName()
{
return getCurrentClassName(1); // making additional overloaded method call requires +1 offset
}
private static String getCurrentClassName(int offset)
{
return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getClassName();
}
public static String getCurrentFileName()
{
return getCurrentFileName(1); // making additional overloaded method call requires +1 offset
}
private static String getCurrentFileName(int offset)
{
String filename = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getFileName();
int lineNumber = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getLineNumber();
return filename + ":" + lineNumber;
}
public static String getInvokingMethodName()
{
return getInvokingMethodName(2);
}
private static String getInvokingMethodName(int offset)
{
return getCurrentMethodName(offset + 1); // re-uses getCurrentMethodName() with desired index
}
public static String getInvokingClassName()
{
return getInvokingClassName(2);
}
private static String getInvokingClassName(int offset)
{
return getCurrentClassName(offset + 1); // re-uses getCurrentClassName() with desired index
}
public static String getInvokingFileName()
{
return getInvokingFileName(2);
}
private static String getInvokingFileName(int offset)
{
return getCurrentFileName(offset + 1); // re-uses getCurrentFileName() with desired index
}
public static String getCurrentMethodNameFqn()
{
return getCurrentMethodNameFqn(1);
}
private static String getCurrentMethodNameFqn(int offset)
{
String currentClassName = getCurrentClassName(offset + 1);
String currentMethodName = getCurrentMethodName(offset + 1);
return currentClassName + "." + currentMethodName ;
}
public static String getCurrentFileNameFqn()
{
String CurrentMethodNameFqn = getCurrentMethodNameFqn(1);
String currentFileName = getCurrentFileName(1);
return CurrentMethodNameFqn + "(" + currentFileName + ")";
}
public static String getInvokingMethodNameFqn()
{
return getInvokingMethodNameFqn(2);
}
private static String getInvokingMethodNameFqn(int offset)
{
String invokingClassName = getInvokingClassName(offset + 1);
String invokingMethodName = getInvokingMethodName(offset + 1);
return invokingClassName + "." + invokingMethodName;
}
public static String getInvokingFileNameFqn()
{
String invokingMethodNameFqn = getInvokingMethodNameFqn(2);
String invokingFileName = getInvokingFileName(2);
return invokingMethodNameFqn + "(" + invokingFileName + ")";
}
}
現在のメソッドを呼び出したメソッドの名前を取得するには、次のようにします。
new Exception("is not thrown").getStackTrace()[1].getMethodName()
これは私のMacBookだけでなく私のAndroid携帯電話でも動作します
私も試してみました:
Thread.currentThread().getStackTrace()[1]
しかし、Androidは "getStackTrace"を返します。
Thread.currentThread().getStackTrace()[2]
しかし、それから私は私のMacBookで間違った答えを得ます
Util.Java:
public static String getCurrentClassAndMethodNames() {
final StackTraceElement e = Thread.currentThread().getStackTrace()[2];
final String s = e.getClassName();
return s.substring(s.lastIndexOf('.') + 1, s.length()) + "." + e.getMethodName();
}
SomeClass.Java:
public class SomeClass {
public static void main(String[] args) {
System.out.println(Util.getCurrentClassAndMethodNames()); // output: SomeClass.main
}
}
String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);
別の方法は、例外を作成するが、例外をスローせず、そのオブジェクトを使用してスタックトレースデータを取得することです。これは、囲むメソッドがtypicallyインデックス0にある-JVMがその情報を保存している限り、他の人が上で述べたように。ただし、これは最も安価な方法ではありません。
Throwable.getStackTrace() から(これは少なくともJava 5以降同じです):
配列の0番目の要素(配列の長さがゼロ以外の場合)は、スタックの最上位を表します。これは、シーケンス内の最後のメソッド呼び出しです。 Typically、これはこのスロー可能オブジェクトが作成およびスローされた時点です。
以下のスニペットは、クラスが非静的であることを前提としています(getClass()による)が、それは別です。
System.out.printf("Class %s.%s\n", getClass().getName(), new Exception("is not thrown").getStackTrace()[0].getMethodName());
私はこれを使用して解決策を持っています(Androidの場合)
/**
* @param className fully qualified className
* <br/>
* <code>YourClassName.class.getName();</code>
* <br/><br/>
* @param classSimpleName simpleClassName
* <br/>
* <code>YourClassName.class.getSimpleName();</code>
* <br/><br/>
*/
public static void getStackTrace(final String className, final String classSimpleName) {
final StackTraceElement[] steArray = Thread.currentThread().getStackTrace();
int index = 0;
for (StackTraceElement ste : steArray) {
if (ste.getClassName().equals(className)) {
break;
}
index++;
}
if (index >= steArray.length) {
// Little Hacky
Log.w(classSimpleName, Arrays.toString(new String[]{steArray[3].getMethodName(), String.valueOf(steArray[3].getLineNumber())}));
} else {
// Legitimate
Log.w(classSimpleName, Arrays.toString(new String[]{steArray[index].getMethodName(), String.valueOf(steArray[index].getLineNumber())}));
}
}
現在実行されているメソッドの名前を取得することの意図が何であるかはわかりませんが、それがデバッグ目的だけの場合は、 "logback"のようなロギングフレームワークが役に立ちます。たとえば、ログバックでは、 ロギング設定でパターン "%M"を使用するだけです 。ただし、これはパフォーマンスを低下させる可能性があるため、注意して使用する必要があります。
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
これは、Java 9以降、 StackWalker
を使用して実行できます。
public static String getCurrentMethodName() {
return StackWalker.getInstance()
.walk(s -> s.skip(1).findFirst())
.get()
.getMethodName();
}
public static String getCallerMethodName() {
return StackWalker.getInstance()
.walk(s -> s.skip(2).findFirst())
.get()
.getMethodName();
}
StackWalker
は遅延するように設計されているので、コールスタック全体の配列を熱心に作成するThread.getStackTrace
より効率的である可能性があります。 詳細についてはJEPも参照してください。
念のため、名前が知りたいメソッドがjunitテストメソッドであれば、junit TestNameルールを使用できます。 https://stackoverflow.com/a/1426730/3076107