スタックトレースまたはリフレクションを使用してメソッドの呼び出し元を見つけるにはどうすればよいですか?


392

メソッドの呼び出し元を見つける必要があります。スタックトレースまたはリフレクションを使用することは可能ですか?


5
ただ疑問に思っていますが、なぜこれを行う必要があるのですか?
ジュリエット

2
通知イベントを持つ親クラス(MVCモデル)があり、サブクラスのセッターだけがこのメソッドを呼び出します。冗長な引数でコードを散らかしたくありません。親クラスのメソッドに、それを呼び出したセッターを見つけさせたい。
サティシュ2009年

30
@Sathishそのデザインを再考する必要があるように聞こえます
krosenvold

7
@Juliet大量のコードをリファクタリングする一環として、最近、多くのもので使用されるメソッドを変更しました。コードが新しいメソッドを適切に使用しているかどうかを検出する特定の方法があるため、それらの場合にそれを呼び出したクラスと行番号を出力していました。ロギング以外では、私はこのような何かのための本当の目的を見ていません。APIを記述したいのですがDontNameYourMethodFooException、呼び出しメソッドの名前がfooの場合にスローされます。
ランチャー、2014年

5
私は自分のメソッドの呼び出し元を非常に貴重なデバッグツールにできることがわかりました。これが、Web検索によってここにもたらされた方法です。私のメソッドが複数の場所から呼び出されている場合、適切な場所から適切なタイミングで呼び出されていますか?@Cruncherが言及しているように、デバッグまたはロギング以外では、有用性はせいぜい限られています。
Ogre Psalm33 2014

回答:


413
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()

Javadocsによると:

配列の最後の要素はスタックの最下部を表します。これは、シーケンス内で最も古いメソッド呼び出しです。

AはStackTraceElementありgetClassName()getFileName()getLineNumber()およびgetMethodName()

必要なインデックスを決定するために実験する必要があります(おそらくstackTraceElements[1]または[2])。


7
getStackTrace()でも例外が作成されるので、これは実際には高速ではなく、より便利です。
マイケルマイヤーズ

42
このメソッドは呼び出し元を与えず、呼び出し元のタイプのみを与えることに注意してください。メソッドを呼び出すオブジェクトへの参照はありません。
Joachim Sauer、

3
余談ですが、1.5 JVMではThread.currentThread()。getStackTrace()は、新しいException()を作成するよりもかなり遅いようです(約3倍遅い)。ただし、すでに述べたように、パフォーマンスが重要な領域では、このようなコードを使用するべきではありません。;)1.6 JVMは10%ほど遅いようですが、Software Monkeyが言ったように、「新しい例外」よりも意図がよく表現されています。
GaZ 2009

21
@Eelco Thread.currentThread()は安価です。Thread.getStackTrace()は高価です。これは、Throwable.fillInStackTrace()とは異なり、調べるメソッドと同じスレッドによってメソッドが呼び出される保証がないため、JVMは「セーフポイント」を作成する必要があるため、ヒープとスタックをロックします。次のバグレポートを参照してください:bugs.sun.com/bugdatabase/view_bug.do?bug_id
David Moles

7
@JoachimSauerメソッドを呼び出すオブジェクトへの参照を取得する方法を知っていますか?
jophde

216

代替ソリューションは、この機能強化のリクエストに対するコメントで見つけることができます。getClassContext()カスタムの方法を使用SecurityManagerおり、スタックトレースメソッドよりも高速のようです。

次のプログラムは、推奨されるさまざまなメソッドの速度をテストします(最も興味深いビットは内部クラスですSecurityManagerMethod)。

/**
 * Test the speed of various methods for getting the caller class name
 */
public class TestGetCallerClassName {

  /**
   * Abstract class for testing different methods of getting the caller class name
   */
  private static abstract class GetCallerClassNameMethod {
      public abstract String getCallerClassName(int callStackDepth);
      public abstract String getMethodName();
  }

  /**
   * Uses the internal Reflection class
   */
  private static class ReflectionMethod extends GetCallerClassNameMethod {
      public String getCallerClassName(int callStackDepth) {
          return sun.reflect.Reflection.getCallerClass(callStackDepth).getName();
      }

      public String getMethodName() {
          return "Reflection";
      }
  }

  /**
   * Get a stack trace from the current thread
   */
  private static class ThreadStackTraceMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return Thread.currentThread().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Current Thread StackTrace";
      }
  }

  /**
   * Get a stack trace from a new Throwable
   */
  private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod {

      public String getCallerClassName(int callStackDepth) {
          return new Throwable().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Throwable StackTrace";
      }
  }

  /**
   * Use the SecurityManager.getClassContext()
   */
  private static class SecurityManagerMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return mySecurityManager.getCallerClassName(callStackDepth);
      }

      public String getMethodName() {
          return "SecurityManager";
      }

      /** 
       * A custom security manager that exposes the getClassContext() information
       */
      static class MySecurityManager extends SecurityManager {
          public String getCallerClassName(int callStackDepth) {
              return getClassContext()[callStackDepth].getName();
          }
      }

      private final static MySecurityManager mySecurityManager =
          new MySecurityManager();
  }

  /**
   * Test all four methods
   */
  public static void main(String[] args) {
      testMethod(new ReflectionMethod());
      testMethod(new ThreadStackTraceMethod());
      testMethod(new ThrowableStackTraceMethod());
      testMethod(new SecurityManagerMethod());
  }

  private static void testMethod(GetCallerClassNameMethod method) {
      long startTime = System.nanoTime();
      String className = null;
      for (int i = 0; i < 1000000; i++) {
          className = method.getCallerClassName(2);
      }
      printElapsedTime(method.getMethodName(), startTime);
  }

  private static void printElapsedTime(String title, long startTime) {
      System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms.");
  }
}

Java 1.6.0_17を実行している私の2.4 GHz Intel Core 2 Duo MacBookからの出力の例:

Reflection: 10.195 ms.
Current Thread StackTrace: 5886.964 ms.
Throwable StackTrace: 4700.073 ms.
SecurityManager: 1046.804 ms.

内部反射法は、他の反射法よりもはるかに高速です。新しく作成されたものからスタックトレースを取得するThrowable方が、現在のトレースから取得するよりも高速ですThread。そして、呼び出し元クラスを見つけるための非内部的な方法の中でカスタムSecurityManagerは最速のようです。

更新

以下のようlyomiがで指摘このコメントsun.reflect.Reflection.getCallerClass()メソッドは、Java 7のアップデート40で、デフォルトでは無効とにより、この程度のJava 8.読むには完全に除去されたJavaのバグデータベースでこの問題

アップデート2

以下のようzammbiが発見した、オラクルがされた変更を取り消すことを強制削除しましたsun.reflect.Reflection.getCallerClass()。Java 8でも引き続き使用できます(ただし、非推奨です)。

アップデート3

3年後:現在のJVMでのタイミングの更新。

> java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
> java TestGetCallerClassName
Reflection: 0.194s.
Current Thread StackTrace: 3.887s.
Throwable StackTrace: 3.173s.
SecurityManager: 0.565s.

5
はい、そのようです。ただし、この例で示すタイミングは100万回の呼び出しに対するものであることに注意してください。このため、これをどのように使用しているかによっては、問題にならない場合があります。
Johan Kaving

1
プロジェクトから反射を取り除くと、速度が10倍向上しました。
Kevin Parker

1
はい、一般的にリフレクションは低速ですが(例:stackoverflow.com/questions/435553/java-reflection-performanceを参照)、この特定のケースでは、内部のsun.reflect.Reflectionクラスを使用するのが最も高速です。
Johan Kaving

1
実際には必要ありません。上記のコードを変更して、返されたclassNameを出力することで確認できます(ループカウントを1に減らすことをお勧めします)。すべてのメソッドが同じclassName-TestGetCallerClassNameを返すことがわかります。
Johan Kaving

1
getCallerClassは非推奨で、7u40で削除されます。悲しい:(
lyomi 2013

36

thisメソッドへの参照を渡さないようにしようとしているように思えます。渡すことthisは、現在のスタックトレースから呼び出し元を見つけるよりもはるかに優れています。 よりオブジェクト指向の設計へのリファクタリングはさらに優れています。 発信者を知る必要はありません。必要に応じて、コールバックオブジェクトを渡します。


6
++呼び出し元を知ることはあまりにも多くの情報です。必要であれば、インターフェースを渡すこともできますが、大きなリファクタリングが必要になる可能性は十分にあります。@satishは彼のコードを投稿し、それを少し楽しんでもらいましょう:)
Bill K

15
これを実行したい正当な理由が存在します。たとえば、テスト中に役立つことが何度かありました。
Eelco

2
@chillenious知っています:) LoggerFactory.getLogger(MyClass.class)クラスリテラルを渡す必要がない場所のようなメソッドを自分で作成しました。それがまだ正しいことはめったにありません。
Craig P. Motlin、2010

6
これは一般的には良いアドバイスですが、質問には答えません。
Navin

1
呼び出し元に関する情報を取得することが適切な設計上の決定である場合の具体例は、.NET INotifyPropertyChangedインターフェイスを実装する場合です。この特定の例はJavaにはありませんが、フィールド/ゲッターをReflectionの文字列としてモデル化しようとすると、同じ問題が発生する可能性があります。
Chris Kerekes

31

Java 9-JEP 259:Stack-Walking API

JEP 259はスタックウォーキング用の効率的な標準APIを提供し、スタックトレース内の情報を簡単にフィルタリングして遅延アクセスできるようにします。Stack-Walking API以前は、スタックフレームにアクセスする一般的な方法は次のとおりでした。

Throwable::getStackTraceThread::getStackTrace配列を返します StackTraceElement、各スタックトレース要素のクラス名とメソッド名を含むオブジェクトます。

SecurityManager::getClassContext 保護されたメソッドであり、 SecurityManagerサブクラスがクラスコンテキストにアクセスです。

sun.reflect.Reflection::getCallerClassとにかく使用すべきではないJDK内部メソッド

これらのAPIの使用は通常非効率的です。

これらのAPIは、スタック全体のスナップショットを熱心にキャプチャするVMを必要とし、スタック全体を表す情報を返します。呼び出し元がスタックの上位数フレームのみに関心がある場合は、すべてのフレームを調べるコストを回避する方法はありません。

直接の呼び出し元のクラスを見つけるには、最初にStackWalker

StackWalker walker = StackWalker
                           .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

次に、次のいずれかを呼び出しますgetCallerClass()

Class<?> callerClass = walker.getCallerClass();

またはSとは、最初に、前を取得します:walkStackFrameStackFrame

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());

15

Oneliner

Thread.currentThread().getStackTrace()[2].getMethodName()

2を1に置き換える必要がある場合があることに注意してください。


10

このメソッドは同じことを行いますが、もう少し単純で、おそらくもう少しパフォーマンスが高く、リフレクションを使用している場合は、それらのフレームを自動的にスキップします。唯一の問題は、Sun以外のJVMには存在しない可能性があることですが、JRockit 1.4-> 1.6のランタイムクラスに含まれています。(要点は、パブリッククラスではない)。

sun.reflect.Reflection

    /** Returns the class of the method <code>realFramesToSkip</code>
        frames up the stack (zero-based), ignoring frames associated
        with java.lang.reflect.Method.invoke() and its implementation.
        The first frame is that associated with this method, so
        <code>getCallerClass(0)</code> returns the Class object for
        sun.reflect.Reflection. Frames associated with
        java.lang.reflect.Method.invoke() and its implementation are
        completely ignored and do not count toward the number of "real"
        frames skipped. */
    public static native Class getCallerClass(int realFramesToSkip);

realFramesToSkip値がどうあるべきか、Sun 1.5および1.6 VMバージョンのjava.lang.Systemには、getCallerClass()と呼ばれるパッケージ保護されたメソッドsun.reflect.Reflection.getCallerClass(3)がありますが、私のヘルパーユーティリティクラスでは、ヘルパークラスの追加フレームがあるため、4を使用しました呼び出し。


16
JVM実装クラスの使用は本当に悪い考えです。
Lawrence Dol

7
了解しました。私はそれがパブリッククラスではなく、java.lang.Systemの保護されたメソッドgetCallerClass()が、IBM、JRockit、Sunを含む、私が調べたすべての1.5以上のVMに存在することを指定しましたが、あなたの主張は控えめに言って健全です。
ニコラス

6
@Software Monkeyは、いつものように、「すべてに依存します」。デバッグやログのテストを支援するためにこのようなことを行うと、特にそれが本番用コードで終わらない場合、またはデプロイメントターゲットが厳密に開発者のPCである場合は、おそらく問題ありません。そのような場合でもまだ他の方法で考える人:「本当に悪いアイデア」を単に悪いと言うよりも理に

8
また、同様のロジックにより、JPAと互換性のないHibernate固有の機能をいつでも使用できると主張することもできますが、これは常に「非常に悪い考え」です。または、他のデータベースでは利用できないOracle固有の機能を使用する場合は、「非常に悪い考え」です。確かにそれは...ええと、より安全な発想と確かに、特定の用途のために良いアドバイスですが、自動的にだけ、それはあなたがしていることをソフトウェア構成では動作しませんので、離れた便利なツールを投げまったく使っていませんか?それは少し柔軟性がなく、少しばかげています。

5
ベンダー固有のクラスを無防備に使用すると、問題が発生する可能性が高くなりますが、問題のクラスが存在しない(または何らかの理由で禁止されている)場合は、適切に機能を低下させる方法を決定する必要があります。ベンダー固有のクラスの使用を全面的に拒否するポリシーは、私の意見では、少し素朴です。本番環境で使用するいくつかのライブラリのソースコードをざっと見て、それらのいずれかがこれを行うかどうかを確認します。(sun.misc.Unsafe多分?)
ニコラス

7
     /**
       * 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 = new Throwable().getStackTrace();

        //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
        return ste[ste.length - depth].getMethodName();
      }

たとえば、デバッグ目的で呼び出しメソッド行を取得しようとする場合、これらの静的メソッドをコーディングするUtilityクラスを通過する必要があります
(古いjava1.4コード、潜在的なStackTraceElementの使用法を説明するためだけ)

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils". <br />
          * From the Stack Trace.
          * @return "[class#method(line)]: " (never empty, first class past StackTraceUtils)
          */
        public static String getClassMethodLine()
        {
            return getClassMethodLine(null);
        }

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass. <br />
          * Allows to get past a certain class.
          * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
          * @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils)
          */
        public static String getClassMethodLine(final Class aclass)
        {
            final StackTraceElement st = getCallingStackTraceElement(aclass);
            final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber()
            +")] <" + Thread.currentThread().getName() + ">: ";
            return amsg;
        }

     /**
       * Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass. <br />
       * Stored in array of the callstack. <br />
       * Allows to get past a certain class.
       * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
       * @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils)
       * @throws AssertionFailedException if resulting statckTrace is null (RuntimeException)
       */
      public static StackTraceElement getCallingStackTraceElement(final Class aclass)
      {
        final Throwable           t         = new Throwable();
        final StackTraceElement[] ste       = t.getStackTrace();
        int index = 1;
        final int limit = ste.length;
        StackTraceElement   st        = ste[index];
        String              className = st.getClassName();
        boolean aclassfound = false;
        if(aclass == null)
        {
            aclassfound = true;
        }
        StackTraceElement   resst = null;
        while(index < limit)
        {
            if(shouldExamine(className, aclass) == true)
            {
                if(resst == null)
                {
                    resst = st;
                }
                if(aclassfound == true)
                {
                    final StackTraceElement ast = onClassfound(aclass, className, st);
                    if(ast != null)
                    {
                        resst = ast;
                        break;
                    }
                }
                else
                {
                    if(aclass != null && aclass.getName().equals(className) == true)
                    {
                        aclassfound = true;
                    }
                }
            }
            index = index + 1;
            st        = ste[index];
            className = st.getClassName();
        }
        if(resst == null) 
        {
            //Assert.isNotNull(resst, "stack trace should null"); //NO OTHERWISE circular dependencies 
            throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$
        }
        return resst;
      }

      static private boolean shouldExamine(String className, Class aclass)
      {
          final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith("LogUtils"
            ) == false || (aclass !=null && aclass.getName().endsWith("LogUtils")));
          return res;
      }

      static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st)
      {
          StackTraceElement   resst = null;
          if(aclass != null && aclass.getName().equals(className) == false)
          {
              resst = st;
          }
          if(aclass == null)
          {
              resst = st;
          }
          return resst;
      }

Java 1.4で動作するものが必要でしたが、この回答は非常に役に立ちました。ありがとうございました!
RGO 2014年

6

私は以前にこれをやったことがあります。新しい例外を作成して、それをスローせずにスタックトレースを取得し、スタックトレースを調べることができます。しかし、他の答えが言うように、それは非常にコストがかかります-タイトなループでそれをしないでください。

ボタンのクリックなどのアクションの結果を表示する限り、パフォーマンスがそれほど重要ではないアプリのロギングユーティリティで以前にそれを実行しました(実際、パフォーマンスはほとんど重要ではありません)。

スタックトレースを取得する前でした。例外には.printStackTrace()が含まれていたため、System.outを自分の作成したストリームにリダイレクトし、次に(new Exception())。printStackTrace();にしました。System.outをリダイレクトしてストリームを解析します。楽しいもの。


涼しい; 投げる必要はありませんか?
krosenvold 2009年

いいえ、少なくともそれは私の記憶です。数年は行っていませんが、例外の新規作成はオブジェクトを作成するだけであり、例外をスローしてもパス以外は何も実行されません。 catch()句に追加します。
ビルK

きちんと。私は実際の例外をシミュレートするためにそれを投げる傾向がありました。
サティシュ2009年

いいえ。Java5の場合、現在のスタックをStackTraceElementsの配列として取得するメソッドがThreadにあります。それはまだ安価ではありませんが、古い例外解析ソリューションよりも安価です。
Lawrence Dol、

@Software Monkeyより適切だと私は確信していますが、それがより安いとあなたは何が言うのですか?同じメカニズムが使用されると思いますが、そうでない場合、同じことをするのになぜ遅くするのですか?
ビルK

1
private void parseExceptionContents(
      final Exception exception,
      final OutputStream out)
   {
      final StackTraceElement[] stackTrace = exception.getStackTrace();
      int index = 0;
      for (StackTraceElement element : stackTrace)
      {
         final String exceptionMsg =
              "Exception thrown from " + element.getMethodName()
            + " in class " + element.getClassName() + " [on line number "
            + element.getLineNumber() + " of file " + element.getFileName() + "]";
         try
         {
            out.write((headerLine + newLine).getBytes());
            out.write((headerTitlePortion + index++ + newLine).getBytes() );
            out.write((headerLine + newLine).getBytes());
            out.write((exceptionMsg + newLine + newLine).getBytes());
            out.write(
               ("Exception.toString: " + element.toString() + newLine).getBytes());
         }
         catch (IOException ioEx)
         {
            System.err.println(
                 "IOException encountered while trying to write "
               + "StackTraceElement data to provided OutputStream.\n"
               + ioEx.getMessage() );
         }
      }
   }

0

これは、このトピックで示したヒントに基づいて私が作成したコードの一部です。それが役に立てば幸い。

(このコードを改善するための提案は自由に行ってください。教えてください)

カウンタ:

public class InstanceCount{
    private static Map<Integer, CounterInstanceLog> instanceMap = new HashMap<Integer, CounterInstanceLog>();
private CounterInstanceLog counterInstanceLog;


    public void count() {
        counterInstanceLog= new counterInstanceLog();
    if(counterInstanceLog.getIdHashCode() != 0){
    try {
        if (instanceMap .containsKey(counterInstanceLog.getIdHashCode())) {
         counterInstanceLog= instanceMap .get(counterInstanceLog.getIdHashCode());
    }

    counterInstanceLog.incrementCounter();

            instanceMap .put(counterInstanceLog.getIdHashCode(), counterInstanceLog);
    }

    (...)
}

そしてオブジェクト:

public class CounterInstanceLog{
    private int idHashCode;
    private StackTraceElement[] arrayStackTraceElements;
    private int instanceCount;
    private String callerClassName;

    private StackTraceElement getProjectClasses(int depth) {
      if(depth< 10){
        getCallerClassName(sun.reflect.Reflection.getCallerClass(depth).getName());
        if(getCallerClassName().startsWith("com.yourproject.model")){
            setStackTraceElements(Thread.currentThread().getStackTrace());
            setIdHashCode();
        return arrayStackTraceElements[depth];
        }
        //+2 because one new item are added to the stackflow
        return getProjectClasses(profundidade+2);           
      }else{
        return null;
      }
    }

    private void setIdHashCode() {
        if(getNomeClasse() != null){
            this.idHashCode = (getCallerClassName()).hashCode();
        }
    }

    public void incrementaContador() {
    this.instanceCount++;
}

    //getters and setters

    (...)



}

0
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

class DBConnection {
    String createdBy = null;

    DBConnection(Throwable whoCreatedMe) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(os);
        whoCreatedMe.printStackTrace(pw);
        try {
            createdBy = os.toString();
            pw.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class ThrowableTest {

    public static void main(String[] args) {

        Throwable createdBy = new Throwable(
                "Connection created from DBConnectionManager");
        DBConnection conn = new DBConnection(createdBy);
        System.out.println(conn.createdBy);
    }
}

または

public static interface ICallback<T> { T doOperation(); }


public class TestCallerOfMethod {

    public static <T> T callTwo(final ICallback<T> c){
        // Pass the object created at callee to the caller
        // From the passed object we can get; what is the callee name like below.
        System.out.println(c.getClass().getEnclosingMethod().getName());
        return c.doOperation();
    }

    public static boolean callOne(){
        ICallback callBackInstance = new ICallback(Boolean){
            @Override
            public Boolean doOperation() 
            {
                return true;
            }
        };
        return callTwo(callBackInstance);
    }

    public static void main(String[] args) {
         callOne();
    }
}

0

この方法を使用してください:

 StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
 stackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
 System.out.println(e.getMethodName());

メソッド例コードの呼び出し元はここにあります-

public class TestString {

    public static void main(String[] args) {
        TestString testString = new TestString();
        testString.doit1();
        testString.doit2();
        testString.doit3();
        testString.doit4();
    }

    public void doit() {
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
        System.out.println(e.getMethodName());
    }

    public void doit1() {
        doit();
    }

    public void doit2() {
        doit();
    }

    public void doit3() {
        doit();
    }

    public void doit4() {
        doit();
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.