Получить имя поля

Возможно ли в Java получить имя поля в строке из фактического поля? как:

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

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

PS: я не ищу имя класса/класса поля или получаю поле из имени в строке.


РЕДАКТИРОВАТЬ: Когда я смотрю на это, мне, вероятно, не понадобится метод для получения имени поля, экземпляра поля (из «кодового имени» поля) будет достаточно. [например. 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));
        }
    }
}

Возникает вопрос: как мог бы выглядеть метод buildItemIdeal? (Из ответов и гугления я думаю, что это невозможно на Java, но кто знает..)


person monnef    schedule 18.02.2013    source источник
comment
Если поле не является приватным, то можно узнать имя поля, на которое ссылается конкретная часть кода, но анализируя байт-код. Для этого вы можете использовать библиотеку ASM.   -  person Mikhail Vladimirov    schedule 19.02.2013
comment
Что вы имеете в виду под названием поля? Это String linkTool в вашем примере?   -  person AlexWien    schedule 19.02.2013
comment
Вы имеете в виду имя поля класса, который вы написали сами, или другого класса?   -  person jlordo    schedule 19.02.2013
comment
Обратите внимание, что ItemLinkTool, вероятно, имеет более одного метода. Что должен вернуть ваш метод getFieldsName в таком случае?   -  person maksimov    schedule 19.02.2013
comment
Я не уверен, что написал это ясно, мне нужна та же функциональность, что и: this.getClass().getField(linkTool) только без строковой части ~ this.getClass().getField(linkTool).   -  person monnef    schedule 19.02.2013
comment
Зачем вам нужно отраженное поле, когда у вас уже есть фактическое поле?   -  person Xyene    schedule 19.02.2013
comment
@Nox: Поскольку на этапе 1 я получаю идентификаторы для полей, помеченных @ItemID (поля пусты, и другие поля будут одного типа), на следующем этапе я хотел бы обращаться к полям по их именам, а не по строкам (я написал причины ниже) . Из имени строки я мог бы получить экземпляр поля, а затем использовать его в качестве индекса на карте, чтобы получить его идентификатор (необходимый в конструкторе класса поля). Основываясь на классе поля (и, возможно, аннотации), я мог бы создать экземпляр и присвоить его значению поля. PS: под Полем я подразумеваю java.lang.reflect.Field, а не любое поле.   -  person monnef    schedule 19.02.2013
comment
Моя мотивация при получении имени поля заключается в том, чтобы использовать его для поиска вещей в базе данных «ключ-значение» (например, mongodb). У меня есть структура с элементами (полями), которая представляет сохраненные данные. Я использую имена членов для поиска в базе данных.   -  person AlikElzin-kilaka    schedule 26.08.2015


Ответы (7)


К сожалению, нет возможности сделать то, что вы хотите. Почему?

В эпоху, когда мы все еще пытаемся доказать, что Java не является медленным, хранение метаданных о том, где и как был создан объект, потребовало бы больших накладных расходов, и, поскольку у него очень минимальный диапазон использования, его не существует. Мало того: есть еще куча технических причин.

Одна из причин связана с тем, как реализована JVM. Спецификацию формата файла Java class можно найти здесь< /а>. Это довольно многословно, но очень информативно.

Как я уже говорил ранее, объект можно создать из любого места, даже из ситуаций, когда объекты не имеют имен. Только объекты, определенные как члены класса, имеют имена (по очевидной причине доступа): объекты, созданные в методах, не имеют. 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).

Это само по себе было бы проблемой для отладчиков и тому подобного, поэтому был разработан набор атрибутов (например, метаданные для файлов классов). К ним относятся LocalVariableTable, LineNumberTable и LocalVariableTypeTable . Эти атрибуты полезны для трассировки стека (LineNumberTable) и отладчиков (LocalVariableTable и LocalVariableTypeTable). Однако их необязательно включать в файлы, и нет никакой гарантии, что они будут:

Атрибут LineNumberTable является необязательным атрибутом переменной длины в таблице атрибутов.

То же самое относится и к остальным из них.

Кроме того, большинство компиляторов по умолчанию не создают ничего, кроме LineNumberTable, так что вам не повезло бы, даже если бы это было возможно.

По сути, разработчикам было бы очень неприятно иметь getFieldName(object) (что было бы невозможно в первую очередь для локальных переменных) и работать только при наличии этих атрибутов.

Итак, в обозримом будущем вы застряли на использовании getField со строками. Бессовестный плагин: IntelliJ, кажется, довольно хорошо справляется с рефакторингом с отражением: я написал моды для Minecraft и знаю, чувство, и могу сказать, что работа по рефакторингу значительно сокращается благодаря IntelliJ. Но то же самое, вероятно, верно и для большинства современных IDE: я уверен, что крупные игроки, Eclipse, Netbeans и др. имеют хорошо реализованные системы рефакторинга.

person Xyene    schedule 20.02.2013
comment
Смотрите мой ответ для получения имени поля. - person Stefan; 30.05.2016
comment
Почему замена имени поля на строковый литерал во время компиляции будет медленной? - person Orestis P.; 13.08.2019

Строка 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;
}
person Stefan    schedule 15.02.2016
comment
Это не будет надежным, если один и тот же экземпляр объекта назначен более чем одному полю. - person Giovanni Lovato; 27.04.2017
comment
Для этого особого случая вам нужно будет вернуть список имен полей. - person Stefan; 03.05.2017

Это возможно только через отражение.

Используйте Class.getDeclaringFields() и java.reflection.Field.getName()

person AlexWien    schedule 18.02.2013
comment
Да, но при использовании отражения мне нужно имя поля в строке в качестве параметра (в реальном классе полей гораздо больше, и они имеют разные типы). Мой вопрос заключался в том, есть ли способ получить экземпляр типа java.lang.reflect.Field только из имени поля в коде (НЕ из строки). - person monnef; 19.02.2013
comment
да, с отражением, получить все объявляющие поля, перебрать этот массив, и первое, которое соответствует вашему имени файла, является правильным. - person AlexWien; 19.02.2013
comment
как получить имя поля в строке из имени поля в коде? посмотрите на новый пример, может на этот раз я лучше написал вопрос. - person monnef; 19.02.2013
comment
@AlexWien в Class.getDeclaringFields() нет такого метода, ответ непонятен. - person Shashi Ranjan; 11.08.2016
comment
@ShashiRanjan Попробуйте имя-вашего-класса.class.getDeclaredFields(). Это возвращает массив полей типа java.reflection.Field, который вы можете перебрать и применить к нему метод .getName(). - person GHOST-34; 07.09.2016

Если поле закрыто, вы можете сделать его доступным:

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.

Если вы не знаете имя поля... ну... тогда какое поле вы хотели бы получить? :) Вы можете получить ВСЕ поля, используя отражение, а затем прокрутить их. Но если вы не знаете поле, каков фактический смысл и логика в этом действии?

person user    schedule 18.02.2013
comment
в том-то и проблема, что при вводе хотелось бы linkTool, а не linkTool. - person monnef; 19.02.2013
comment
Ну, вы не получите имя в кавычках. В чем проблема? - person user; 19.02.2013
comment
Код с интенсивным использованием строк, содержащих идентификаторы полей/классов/методов/пространств имен, непросто поддерживать или рефакторить (обычно он требует ручных действий, у IDE есть проблемы с такими вещами). - person monnef; 19.02.2013
comment
Я попробовал код: поле field = this.getClass().getDeclaredField(field1); field.setAccessible (истина); вернуть поле.getName(); в самом классе POJO, но я получаю NullPointer. field1 является частной строкой field1 - person Shashi Ranjan; 11.08.2016
comment
Вызов object.getClass().getDeclaredField(fieldName) противоречит цели вопроса, которая состоит в том, чтобы получить имя поля из самого объекта. Параметр fieldName в этом вызове будет таким же, как значение, возвращаемое из (String)field.get(object). Согласно вопросу, имя поля априори неизвестно. - person Monte Creasor; 17.10.2017

Я думаю, это то, что вы ищете.

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;
}
person Oguz    schedule 22.05.2018

Моя мотивация при получении имени поля заключается в том, чтобы использовать его для поиска вещей в базе данных «ключ-значение» (например, mongodb). У меня есть структура с элементами (полями), которая представляет сохраненные данные. Я использую имена членов для поиска в базе данных.

Я предлагаю поставить статический геттер для каждого важного члена. Пример:

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

Будем надеяться, что в будущем в Java будет какой-то механизм для этого, потому что в настоящее время метаданные могут быть частью данных. Особенно при работе с JSON.

person AlikElzin-kilaka    schedule 26.08.2015
comment
Где ответ? - person Shashi Ranjan; 11.08.2016

Это можно сделать с помощью инструментария байт-кода во время выполнения, например, с помощью библиотеки Byte Buddy, см. этот ответ имя эквивалента в Java

person jreznot    schedule 22.11.2017