Javaで現在実行中のメソッドの名前を取得する方法はありますか?
23 に答える
技術的にはこれでうまくいきます...
String name = new Object(){}.getClass().getEnclosingMethod().getName();
ただし、コンパイル時に新しい匿名内部クラスが作成されます (例: YourClass$1.class
)。したがって、これにより、.class
このトリックを展開するメソッドごとにファイルが作成されます。さらに、それ以外の場合は使用されないオブジェクト インスタンスが、実行時の各呼び出しで作成されます。したがって、これは許容できるデバッグ トリックかもしれませんが、かなりのオーバーヘッドが伴います。
このトリックの利点は、注釈やパラメーター名を含むメソッドの他のすべての情報を取得するために使用できるものをgetEnclosingMethod()
返すことです。java.lang.reflect.Method
これにより、同じ名前の特定のメソッドを区別できます (メソッド オーバーロード)。
getEnclosingMethod()
このトリックのJavaDocによれば、SecurityException
内部クラスは同じクラスローダーを使用してロードする必要があるため、をスローしないでください。そのため、セキュリティ管理者がいてもアクセス状況を確認する必要はありません。
注意してくださいgetEnclosingConstructor()
:コンストラクターに使用する必要があります。(名前付き) メソッドの外側のブロックの間、 をgetEnclosingMethod()
返しますnull
。
このコードを使用して、スタック トレース インデックスの潜在的な変動を軽減しました。今は、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 になります。
これらのオプションは両方とも、Java で機能します。
new Object(){}.getClass().getEnclosingMethod().getName()
または:
Thread.currentThread().getStackTrace()[1].getMethodName()
私が見つけた最速の方法は次のとおりです。
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(intdepth)に直接アクセスします。そして、アクセス可能なメソッドを静的変数に格納します。
次のコードを使用します。
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
String methodName = e.getMethodName();
System.out.println(methodName);
これは、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
}
}
別の方法は、例外を作成しますが、例外をスローせず、そのオブジェクトを使用してスタック トレース データを取得することです。これは、JVM がその情報を他のメソッドと同様に格納している限り、通常、外側のメソッドはインデックス 0 にあるためです。上記の通り。ただし、これは最も安価な方法ではありません。
Throwable.getStackTrace()から(これは、少なくとも Java 5 以降は同じです):
配列の 0 番目の要素 (配列の長さがゼロでない場合) は、スタックの一番上を表し、これはシーケンス内の最後のメソッド呼び出しです。通常、これは、このスロー可能オブジェクトが作成およびスローされた時点です。
以下のスニペットは、(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())}));
}
}
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
このアプローチの問題点:
class Example {
FileOutputStream fileOutputStream;
public Example() {
//System.out.println("Example.Example()");
debug("Example.Example()",false); // toggle
try {
fileOutputStream = new FileOutputStream("debug.txt");
} catch (Exception exception) {
debug(exception + Calendar.getInstance().getTime());
}
}
private boolean was911AnInsideJob() {
System.out.println("Example.was911AnInsideJob()");
return true;
}
public boolean shouldGWBushBeImpeached(){
System.out.println("Example.shouldGWBushBeImpeached()");
return true;
}
public void setPunishment(int yearsInJail){
debug("Server.setPunishment(int yearsInJail=" + yearsInJail + ")",true);
}
}
そして、人々が使用に夢中になる前にSystem.out.println(...)
、出力をリダイレクトできるように、いつでも何らかのメソッドを作成できますし、作成する必要があります。
private void debug (Object object) {
debug(object,true);
}
private void dedub(Object object, boolean debug) {
if (debug) {
System.out.println(object);
// you can also write to a file but make sure the output stream
// ISN'T opened every time debug(Object object) is called
fileOutputStream.write(object.toString().getBytes());
}
}