Javaリフレクション:変数の名前を取得する方法?


139

Javaリフレクションを使用して、ローカル変数の名前を取得することは可能ですか?たとえば、私がこれを持っている場合:

Foo b = new Foo();
Foo a = new Foo();
Foo r = new Foo();

これらの変数の名前を見つけることができるメソッドを実装することは可能ですか?

public void baz(Foo... foos)
{
    for (Foo foo: foos) {
        // Print the name of each foo - b, a, and r
        System.out.println(***); 
    }
}

編集:この質問は異なりますJavaに関数に渡された変数の名前を見つける方法はありますか?それは、リフレクションを使用してローカル変数の名前を決定できるかどうかについての質問をより純粋に尋ねるのに対し、他の質問(受け入れられた回答を含む)は変数の値のテストにより焦点を当てています。


11
すべての素晴らしい答え!返信およびコメントしてくださった皆さんに感謝します-これは興味深い洞察に満ちた議論でした。
David Koelle


可能です。私の[要旨] [1]を見てください。JDK 1.1からJDK 7まで機能します。[1]:gist.github.com/2011728
Wendal Chen


3
重複ではなく、理由を説明するために私の質問を更新しました。どちらかと言えば、その別の質問はこれの複製(または特別な場合)です!
David Koelle、2015

回答:


65

Java 8以降、一部のローカル変数名情報はリフレクションを通じて利用できます。以下の「更新」セクションを参照してください。

多くの場合、完全な情報はクラスファイルに格納されます。コンパイル時の最適化の1つは、それを削除してスペースを節約する(そして難読化する)ことです。ただし、それが存在する場合、各メソッドには、ローカル変数のタイプと名前、およびそれらがスコープ内にある命令の範囲をリストするローカル変数テーブル属性があります。

おそらく、ASMのようなバイトコードエンジニアリングライブラリを使用すると、実行時にこの情報を検査できます。この情報を必要とするために私が考えることができる唯一の合理的な場所は開発ツール内であり、そのためバイトコードエンジニアリングは他の目的にも役立つ可能性があります。


更新:これに対する限定サポートがJava 8追加されました。パラメーター(ローカル変数の特別なクラス)名は、リフレクションを介して使用できるようになりました。他の目的の中でも、これは@ParameterName依存関係注入コンテナによって使用される注釈を置き換えるのに役立ちます。


49

それは全く不可能です。変数名はJava内で通信されません(コンパイラの最適化のために削除される場合もあります)。

編集(コメントに関連):

関数パラメーターとして使用する必要があるという考えから後退した場合、ここに代替案があります(私は使用しません-以下を参照):

public void printFieldNames(Object obj, Foo... foos) {
    List<Foo> fooList = Arrays.asList(foos);
    for(Field field : obj.getClass().getFields()) {
         if(fooList.contains(field.get()) {
              System.out.println(field.getName());
         }
    }
}

a == b, a == r, or b == r同じ参照を持つ他のフィールドがある場合、問題が発生します。

質問が明確になったため、編集は不要になりました


次に、これをどのように説明します か:java.sun.com/javase/6/docs/api/java/lang/reflect/Field.html
アウトロープログラマ

1
-1:誤解したと思います。@Davidはローカル変数ではなくフィールドの後にあります。ローカル変数は、Reflection APIでは実際には使用できません。
ルークウッドワード

Pourquoi Litytestdataは正しいと思います。当然、フィールドを最適化することはできないため、Marcel J.はローカル変数を考慮している必要があります。
マイケル・マイヤーズ

3
@David:ローカル変数ではなくフィールドを意味することを明確にするために編集する必要があります。元の質問は、b、a、およびrをローカル変数として宣言するコードを示しています。
Jason S

7
私はローカル変数を意味していたので、それを反映するように質問を編集しました。変数名を取得できないのではないかと思ったのですが、不可能だと考える前にSOに聞いてみようと思いました。
David Koelle、

30

編集:以前の2つの回答が削除されました。1つは編集前の質問に回答するためのもので、もう1つは完全に間違っていないとしても、少なくともそれに近いもののためです。

javac -g)のデバッグ情報を使用してコンパイルする場合、ローカル変数の名前は.classファイルに保持されます。たとえば、次の単純なクラスを見てください。

class TestLocalVarNames {
    public String aMethod(int arg) {
        String local1 = "a string";
        StringBuilder local2 = new StringBuilder();
        return local2.append(local1).append(arg).toString();
    }
}

でコンパイルした後javac -g:vars TestLocalVarNames.java、ローカル変数の名前は.classファイルにあります。javap-lフラグ(「行番号とローカル変数テーブルを印刷する」)はそれらを表示できます。

javap -l -c TestLocalVarNames ショー:

class TestLocalVarNames extends java.lang.Object{
TestLocalVarNames();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      5      0    this       LTestLocalVarNames;

public java.lang.String aMethod(int);
  Code:
   0:   ldc     #2; //String a string
   2:   astore_2
   3:   new     #3; //class java/lang/StringBuilder
   6:   dup
   7:   invokespecial   #4; //Method java/lang/StringBuilder."<init>":()V
   10:  astore_3
   11:  aload_3
   12:  aload_2
   13:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   16:  iload_1
   17:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   20:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   23:  areturn

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      24      0    this       LTestLocalVarNames;
   0      24      1    arg       I
   3      21      2    local1       Ljava/lang/String;
   11      13      3    local2       Ljava/lang/StringBuilder;
}

VMの仕様では、私たちがここで見ているものを説明します。

§4.7.9 LocalVariableTable属性

このLocalVariableTable属性は、Code(§4.7.3)属性のオプションの可変長属性です。デバッガーは、メソッドの実行中に特定のローカル変数の値を決定するために使用できます。

LocalVariableTable店舗各スロット内の変数の名前と型なので、バイトコードでそれらを一致させることができます。これは、デバッガーが「式を評価する」方法です。

ただし、エリクソンが言ったように、通常のリフレクションを介してこのテーブルにアクセスする方法はありません。まだこれを行うと決心している場合は、Javaプラットフォームデバッガアーキテクチャ(JPDA)が役立つと思います(ただし、自分で使用したことはありません)。


1
ええと、エリクソンが私が編集している間に投稿しました、そして今私は彼と矛盾しています。それはおそらく私が間違っていることを意味します。
マイケルマイヤーズ

デフォルトでjavacは、各メソッドのクラスにローカル変数テーブルを配置して、デバッグを支援します。-lオプションを使用してjavap、ローカル変数テーブルを表示します。
エリクソン2009

デフォルトではないようです。私はjavac -g:varsそれを得るために使用しなければなりませんでした。(私は過去3時間この回答を編集しようとしましたが、ネットワーク接続に問題があるため、調査が難しくなっています。)
Michael Myers

2
あなたは正しい、それについて申し訳ありません。デフォルトで「オン」になっているのは行番号です。
エリクソン2009

15
import java.lang.reflect.Field;


public class test {

 public int i = 5;

 public Integer test = 5;

 public String omghi = "der";

 public static String testStatic = "THIS IS STATIC";

 public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
  test t = new test();
  for(Field f : t.getClass().getFields()) {
   System.out.println(f.getGenericType() +" "+f.getName() + " = " + f.get(t));
  }
 }

}

3
getDeclaredFields()プライベートフィールドの名前も必要な場合に使用できます
coffeMug

10

あなたはこのようにすることができます:

Field[] fields = YourClass.class.getDeclaredFields();
//gives no of fields
System.out.println(fields.length);         
for (Field field : fields) {
    //gives the names of the fields
    System.out.println(field.getName());   
}

すべてのフィールドを取得するために、あなたの答えはうまくいきます。私が使用するときに1つのフィールドのみを取得するには:YourClass.class.getDeclaredField( "field1"); NullPointerを取得します。それを使用する際の問題は何ですか?getDeclaredFieldメソッドの使用方法
Shashi Ranjan

0

必要なのは、フィールドの配列を作成し、それを以下に示すように必要なクラスに設定することだけです。

Field fld[] = (class name).class.getDeclaredFields();   
for(Field x : fld)
{System.out.println(x);}

例えばあなたがしたなら

Field fld[] = Integer.class.getDeclaredFields();
          for(Field x : fld)
          {System.out.println(x);}

あなたは得るでしょう

public static final int java.lang.Integer.MIN_VALUE
public static final int java.lang.Integer.MAX_VALUE
public static final java.lang.Class java.lang.Integer.TYPE
static final char[] java.lang.Integer.digits
static final char[] java.lang.Integer.DigitTens
static final char[] java.lang.Integer.DigitOnes
static final int[] java.lang.Integer.sizeTable
private static java.lang.String java.lang.Integer.integerCacheHighPropValue
private final int java.lang.Integer.value
public static final int java.lang.Integer.SIZE
private static final long java.lang.Integer.serialVersionUID

0

一般的な@Marcel Jackwerthの回答を更新します。

そして、クラス属性でのみ機能し、メソッド変数では機能しません。

    /**
     * get variable name as string
     * only work with class attributes
     * not work with method variable
     *
     * @param headClass variable name space
     * @param vars      object variable
     * @throws IllegalAccessException
     */
    public static void printFieldNames(Object headClass, Object... vars) throws IllegalAccessException {
        List<Object> fooList = Arrays.asList(vars);
        for (Field field : headClass.getClass().getFields()) {
            if (fooList.contains(field.get(headClass))) {
                System.out.println(field.getGenericType() + " " + field.getName() + " = " + field.get(headClass));
            }
        }
    }

-1

この例を参照してください:

PersonneTest pt=new PersonneTest();
System.out.println(pt.getClass().getDeclaredFields().length);
Field[]x=pt.getClass().getDeclaredFields();
System.out.println(x[1].getName());
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.