76

メソッドの呼び出し元クラスを取得したい、つまり

class foo{

  bar();

}

メソッドバーで、クラス名を取得する必要があり、次のfooメソッドを見つけました。

Class clazz = sun.reflect.Reflection.getCallerClass(1);

しかし、そうだとしても、私getCallerClasspublicそれを呼ぼうとすると、Eclipseは次のように言います。

アクセス制限:必要なライブラリC:\ Program Files \ Java \ jre7 \ lib \ rt.jarの制限により、Reflection型のメソッドgetCallerClass()にアクセスできません。

他に選択肢はありますか?

4

10 に答える 10

115

スタック トレースを生成し、StackTraceElementsの情報を使用できます。

たとえば、ユーティリティ クラスは呼び出し元のクラス名を返すことができます。

public class KDebug {
    public static String getCallerClassName() { 
        StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
        for (int i=1; i<stElements.length; i++) {
            StackTraceElement ste = stElements[i];
            if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf("java.lang.Thread")!=0) {
                return ste.getClassName();
            }
        }
        return null;
     }
}

KDebug.getCallerClassName()から呼び出すとbar()、 が得られます"foo"

ここで、メソッド呼び出しのクラスを知りたいとしますbar(これはもっと興味深いことであり、おそらくあなたが本当に望んでいたことです)。この方法を使用できます:

public static String getCallerCallerClassName() { 
    StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
    String callerClassName = null;
    for (int i=1; i<stElements.length; i++) {
        StackTraceElement ste = stElements[i];
        if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf("java.lang.Thread")!=0) {
            if (callerClassName==null) {
                callerClassName = ste.getClassName();
            } else if (!callerClassName.equals(ste.getClassName())) {
                return ste.getClassName();
            }
        }
    }
    return null;
 }

それはデバッグ用ですか?そうでない場合は、問題に対するより良い解決策があるかもしれません。

于 2012-07-03T08:09:11.753 に答える
49

スタックトレース

これは、探しているものに大きく依存します...しかし、これは、このオブジェクト内でこのメソッドを直接呼び出したクラスとメソッドを取得する必要があります。

  • インデックス 0 = スレッド
  • インデックス 1 = これ
  • インデックス 2 = 直接の呼び出し元、自分自身にすることができます。
  • インデックス 3 ... n = インデックス 2 以下を取得するために互いに呼び出したクラスとメソッド。

クラス/メソッド/ファイル名の場合:

Thread.currentThread().getStackTrace()[2].getClassName();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();

授業のために:

Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())

参考までに: Class.forName() は、ランタイムではない ClassNotFoundException をスローします。キャッチしてみる必要があります。

また、クラス自体内の呼び出しを無視する場合は、その特定のものをチェックするロジックを使用してループを追加する必要があります。

みたいな... (私はこのコードをテストしていないので注意してください)

StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for(int i=2;i<stes.length;i++)
  if(!stes[i].getClassName().equals(this.getClass().getName()))
    return stes[i].getClassName();

スタックウォーカー

スタック ウォーカー スタックフレーム

これは広範なガイドではなく、可能性の例であることに注意してください。

各 StackFrame のクラスを出力します (クラス参照を取得することにより)

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
    .forEach(frame -> System.out.println(frame.getDeclaringClass()));

同じことを行いますが、最初にストリームをリストに収集します。デモンストレーションのみを目的としています。

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
    .walk(stream -> stream.collect(Collectors.toList()))
    .forEach(frame -> System.out.println(frame.getDeclaringClass()));
于 2016-01-22T14:13:27.617 に答える
42

To get caller/called class name use below code, it works fine for me.

String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();
于 2014-01-27T06:25:30.090 に答える
13

SecurityManager保護されたメソッドgetClassContextがあります

SecurityManager を拡張したユーティリティ クラスを作成することで、これにアクセスできます。

public class CallingClass extends SecurityManager {
    public static final CallingClass INSTANCE = new CallingClass();

    public Class[] getCallingClasses() {
        return getClassContext();
    }
}

CallingClass.INSTANCE.getCallingClasses()呼び出し元のクラスを取得するために使用します。

この情報を公開する小さなライブラリ (免責事項: 私のもの) もあります。利用可能な場合は Reflection.getCallerClass を使用し、利用できない場合は SecurityManager にフォールバックします。

于 2016-01-29T11:17:10.493 に答える
3

これは、呼び出し元クラスだけを取得する最も効率的な方法です。他のアプローチでは、スタック ダンプ全体が取得され、クラス名のみが表示されます。

ただし、このクラスsun.*は実際には内部使用のためのものです。これは、他の Java プラットフォームや他の Java バージョンでは動作しない可能性があることを意味します。これが問題かどうかを判断する必要があります。

于 2012-07-03T08:27:49.420 に答える
2

クラス名とメソッド名を取得する方法を示す簡単な例を以下に示します。

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

   void callMe()
   {
      try
      {
         throw new Exception("Who called me?");
      }
      catch( Exception e )
      {
         System.out.println( "I was called by " + 
                             e.getStackTrace()[1].getClassName() + 
                             "." +
                             e.getStackTrace()[1].getMethodName() + 
                             "()!" );
      }
   }

e には、、、getClassName()および...がgetFileName()あります。getLineNumber()getMethodName()

于 2012-07-03T08:13:04.420 に答える
2

OP が遭遇しているエラー メッセージは、単なる Eclipse の機能です。コードを JVM の特定のメーカー (さらにはバージョン) に関連付けたい場合は、 method を効果的に使用できますsun.reflect.Reflection.getCallerClass()。次に、Eclipse の外部でコードをコンパイルするか、この診断をエラーと見なさないように構成できます。

より悪い Eclipse 構成は、次の方法でエラーの発生をすべて無効にすることです。

Project Properties// Java Compiler/Errors/Warningsオンに設定Enable project specific settings//Deprecated and restrited APIまたはForbidden reference (access rules)に設定。WarningIgnore

より良い Eclipse 構成は、次の方法で特定のエラーの発生を無効にすることです。

Project Properties// Java Build Path/展開 /選択 / / /Librariesに設定または/に設定。JRE System LibraryAccess rules:Edit...Add...Resolution:DiscouragedAccessibleRule Patternsun/reflect/Reflection

于 2015-02-28T02:32:10.030 に答える
1

私は現在同じ問題を抱えているので、ここで私がしていることは次のとおりです。

  1. スタック トレースはクラス (クラスローダーを含む) 自体ではなく名前のみを生成するため、stackTrace ではなく com.sun.Reflection を好みます。

  2. このメソッドは非推奨ですが、Java 8 SDK ではまだ使用されています。

// メソッド記述子 #124 (I)Ljava/lang/Class; (非推奨) // 署名: (I)Ljava/lang/Class<*>; @java.lang.Deprecated public static native java.lang.Class getCallerClass(int arg0);

  1. int 引数のないメソッドは非推奨ではありません

// メソッド記述子 #122 ()Ljava/lang/Class; // 署名: ()Ljava/lang/Class<*>; @sun.reflect.CallerSensitive public static native java.lang.Class getCallerClass();

セキュリティ制限を含め、プラットフォームに依存しない必要があるため、柔軟な方法を作成するだけです。

  1. com.sun.Reflection が利用可能かどうかを確認します (セキュリティ例外によりこのメカニズムが無効になります)

  2. 1 が yes の場合、int 引数または int 引数なしでメソッドを取得します。

  3. 2 が「はい」の場合は、それを呼び出します。

3. に達しなかった場合は、スタック トレースを使用して名前を返します。クラスまたは文字列のいずれかを含む特別な結果オブジェクトを使用します。このオブジェクトは、その内容と理由を正確に示します。

[概要] バックアップにスタック トレースを使用し、リフレクションを使用して Eclipse コンパイラの警告をバイパスします。非常にうまく機能します。コードをきれいに保ち、魔法のように機能し、関連する問題を正しく述べています。

私はこれをかなり長い間使用しており、今日は関連する質問を検索したので

于 2015-12-15T09:20:33.983 に答える
1

次のメソッドを使用して、スタックトレースから特定のクラスの呼び出し元を取得しています:

package test.log;

public class CallerClassTest {

    public static void main(final String[] args) {
        final Caller caller = new Caller(new Callee());
        caller.execute();
    }

    private static class Caller {

        private final Callee c;

        public Caller(final Callee c) {
            this.c = c;
        }

        void execute() {
            c.call();
        }
    }

    static class Callee {

        void call() {
            System.out.println(getCallerClassName(this.getClass()));
        }
    }

    /**
     * Searches the current threads stacktrace for the class that called the given class. Returns {@code null} if the
     * calling class could not be found.
     * 
     * @param clazz
     *            the class that has been called
     * 
     * @return the caller that called the class or {@code null}
     */
    public static String getCallerClassName(final Class<?> clazz) {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        final String className = clazz.getName();
        boolean classFound = false;
        for (int i = 1; i < stackTrace.length; i++) {
            final StackTraceElement element = stackTrace[i];
            final String callerClassName = element.getClassName();
            // check if class name is the requested class
            if (callerClassName.equals(className)) classFound = true;
            else if (classFound) return callerClassName;
        }
        return null;
    }

}
于 2016-02-15T13:50:05.390 に答える