web-dev-qa-db-ja.com

フィールドの名前を取得する

Javaで実際のフィールドから文字列のフィールド名を取得することは可能ですか?

_public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

    public void xxx{
        String fieldsName = *getFieldsName(linkTool)*;
    }
}
_

PS:私はnotフィールドのクラス/クラス名を探しているか、文字列の名前からフィールドを取得しています。


編集:私がそれを見ているとき、おそらくフィールドの名前を取得するためのメソッドを必要としないだろう、フィールドインスタンス(フィールドの「コード名」から)で十分でしょう。 [例えば。 Field myField = getField(linkTool)]

Java自体に必要なものはおそらくないでしょう。ASMライブラリを調べますが、最終的にはフィールドの識別子として文字列を使用することになります。 /


EDIT2:私の英語は上手ではありません(しかし、私の母国語でさえこれを説明するのに問題があるでしょう)ので、私はもう一つの例を追加しています。願わくば、もっと明確になります。

_public class mod2 {
    @ItemID
    public static ItemLinkTool linkTool;

    @ItemID
    public static ItemLinkTool linkTool2;

    @ItemID
    public static ItemPipeWrench pipeWrench;

    public void constructItems() {
        // most trivial way
        linkTool = new ItemLinkTool(getId("linkTool"));
        linkTool2 = new ItemLinkTool(getId("linkTool2"));
        pipeWrench = new ItemPipeWrench(getId("pipeWrench"));

        // or when constructItem would directly write into field just
        constructItem("linkTool");
        constructItem("linkTool2");
        constructItem("pipeWrench");

        // but I'd like to be able to have it like this
        constructItemIdeal(linkTool);
        constructItemIdeal(linkTool2);
        constructItemIdeal(pipeWrench);
    }

    // not tested, just example of how I see it
    private void constructItem(String name){
        Field f = getClass().getField(name);
        int id = getId(name);

        // this could be rewritten if constructors take same parameters
        // to create a new instance using reflection
        if (f.getDeclaringClass() == ItemLinkTool){
            f.set(null, new ItemLinkTool(id));
        }else{
            f.set(null, new ItemPipeWrench(id));
        }
    }
}
_

問題は、constructItemIdealメソッドをどのように見せるかということです。 (答えと周りをグーグルで調べると、Javaでは不可能だと思っていますが、誰が知っていますか..)

27
monnef

残念ながら、あなたが望むことをする方法はありません。 なぜ?

Javaが遅くないことを証明しようとしている時代では、オブジェクトが構築された場所または方法のメタデータを保存すると、オーバーヘッドが大きくなります。それだけでなく、技術的な理由もたくさんあります。

1つの理由は、JVMの実装方法が原因です。 Java classファイル形式の仕様を見つけることができます here 。これは非常に口が広いですが、非常に有益です。

前に言ったように、オブジェクトはどこからでも構築できます。オブジェクトに名前がない場合でも。クラスメンバとして定義されたオブジェクトのみに名前があります(アクセスの明白な理由のため):メソッドで構築されたオブジェクトには名前がありません。 JVMには、最大65535エントリのローカル変数テーブルがあります。これらは、スタックにロードされ、* loadおよび* storeオペコードを介してテーブルに保存されます。

つまり、次のようなクラス

_public class Test {
int class_member = 42;

   public Test() {
      int my_local_field = 42;
   }
}
_

にコンパイルされます

_public class Test extends Java.lang.Object
  SourceFile: "Test.Java"
  minor version: 0
  major version: 50
  Constant pool:
  --snip--

{
int class_member;

public Test();
  Code:
   Stack=2, Locals=2, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method Java/lang/Object."<init>":()V
   4:   aload_0
   5:   bipush  42
   7:   putfield        #2; //Field class_member:I
   10:  bipush  42
   12:  istore_1
   13:  return
  LineNumberTable:
   --snip--    
}
_

そこには、_javap -v Test_からの明確な例を見ることができます。名前_class_member_は保存されますが、_my_local_field_はローカル変数テーブルのインデックス1に抽象化されています(0はthis)。

これ自体はデバッガーなどにとっては苦痛であるため、一連の属性(クラスファイルのメタデータを考える)が設計されました。これらには、 LocalVariableTableLineNumberTable 、および LocalVariableTypeTable が含まれます。これらの属性は、スタックトレース(LineNumberTable)およびデバッガー(LocalVariableTableおよびLocalVariableTypeTable)に役立ちます。ただし、これらはファイルに含めるための完全にオプションのであり、次のことを保証するものではありません:

LineNumberTable属性は、属性テーブルのオプションの可変長属性です

同じことが他の人にも当てはまります。

さらに、ほとんどのコンパイラーはデフォルトでLineNumberTable以外を生成しないため、たとえ可能であっても運が悪いでしょう。

基本的に、開発者がgetFieldName(object)(ローカル変数では最初は不可能)を持ち、それらの属性が存在する場合にのみ機能するのは非常にイライラするでしょう。

したがって、文字列でgetFieldを使用することで、近い将来に行き詰まってしまいます。恥知らずのプラグイン: IntelliJ はリファクタリングをリフレクションで非常にうまく処理しているようです。しかし、ほとんどの現代のIDEには同じことがおそらく当てはまります。Eclipse、Netbeansなどの大物プレイヤーは、場所。

16
Xyene

String fieldName = getFieldName(linkTool、this);

private static String getFieldName(Object fieldObject, Object parent) {

    Java.lang.reflect.Field[] allFields = parent.getClass().getFields();
    for (Java.lang.reflect.Field field : allFields) {
        Object currentFieldObject;
        try {
            currentFieldObject = field.get(parent);
        } catch (Exception e) {
            return null;
        }
        boolean isWantedField = fieldObject.equals(currentFieldObject);
        if (isWantedField) {
            String fieldName = field.getName();
            return fieldName;
        }
    }
    return null;
}
8
Stefan

フィールドがプライベートの場合、アクセス可能にすることができます:

Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
String value = (String)field.get(object); // to get the value, if needed. In the example is string type

フィールドの名前を取得するには、次のようにします。

//use the field object from the example above
field.getName(); // it is in String.

フィールドの名前がわからない場合は...まあ..どのフィールドを取得しますか? :)リフレクションを使用してすべてのフィールドを取得し、それらをループすることができます。しかし、フィールドがわからない場合、このアクションの実際の意味とロジックは何ですか?

7
user

これは、リフレクションを介してのみ可能です。

_Class.getDeclaringFields_()およびJava.reflection.Field.getName()を使用します

6
AlexWien

これがあなたが探しているものだと思います。

SomeClass data; // or List<SomeClass> data; etc.
List<String> fieldNames = getFieldNamesForClass(data.get(0).getClass());

private static List<String> getFieldNamesForClass(Class<?> clazz) throws Exception {
    List<String> fieldNames = new ArrayList<String>();
    Field[] fields = clazz.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
        fieldNames.add(fields[i].getName());
    }
    return fieldNames;
}
6
Oguz

フィールド名を取得する動機は、フィールド名を使用してキー値データベース(mongodbなど)の項目を検索することです。保存されたデータを表すメンバー(フィールド)を持つ構造体があります。 dbの検索にメンバー名を使用

私の提案は、重要なメンバーごとに静的なゲッターを配置することです。例:

public class Data {
  public static String getMember1Key() {
    return "member1";
  }
  public String member1;
}

うまくいけば、Javaは将来、このための何らかのメカニズムを持つでしょう。なぜなら、今日では、メタデータはデータの一部である可能性があります。特にJSONを使用する場合は。

1

これは、Byte Buddyライブラリを使用するなど、ランタイムバイトコードインスツルメンテーションを使用して実行できます。この回答を参照してください Javaの同等の名前

0
jreznot