43

Is it possible in Java to get a name of field in string from the actual field? like:

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

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

PS: I'm not looking for a field's class/class name or getting Field from name in String.


EDIT: When I'm looking at it I probably wouldn't need a method to get a field's name, the Field instance (from field's "code name") would suffice. [e.g. Field myField = getField(linkTool)]

There probably isn't anything like I want in Java itself. I'll take a look at the ASM library, but in the end I might end up using the string as an identifier for fields :/


EDIT2: My english isn't great (but even in my native language I'd have problems explaining this), so I'm adding one more example. Hopefuly it will be more clear now:

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));
        }
    }
}

The question is: how could look the constructItemIdeal method? (From answers and googling around I thing it's not possible in Java, but who knows..)

monnef
  • 3,903
  • 5
  • 30
  • 50
  • If field is not private, then is possible to know the name of the field being referred from particular part of code, but analyzing bytecode. You can use ASM library for this. – Mikhail Vladimirov Feb 18 '13 at 20:03
  • 1
    What do you mean with name of the field? Is that String "linkTool" in your example? – AlexWien Feb 18 '13 at 20:05
  • Do you mean name of a field of a class you wrote yourself or another class? – jlordo Feb 18 '13 at 20:07
  • Note that `ItemLinkTool` probably has more than just one method. What should your `getFieldsName` method return in case like that? – maksimov Feb 18 '13 at 20:09
  • I'm not sure if I wrote it clearly, I'm want same functionality as: this.getClass().getField("linkTool") just without the string part ~ this.getClass().getField(linkTool). – monnef Feb 18 '13 at 20:47
  • Why would you need a reflected Field when you already have the actual field? – Xyene Feb 18 '13 at 20:57
  • @Nox: Because in phase 1 I get IDs for fields marked with `@ItemID` (fields are null and more fields will be of same type), in next phase I'd like to address fields by their names not strings (I wrote reasons below). From string name I would be able to get a Field instance and then use it as an index in a map to get its ID (needed in Field's class's constructor). Based on Field's class (and probably annotation) I would be able to create an instance and assign it to a Field's value. PS: by **Field** I mean java.lang.reflect.Field, not any field. – monnef Feb 19 '13 at 12:05
  • My motivation at getting the field name is using it to search for things in a key-value database (like mongodb). I have a struct with members (fields) that represents the saved data. **I use the member names to search the db**. – AlikElzin-kilaka Aug 26 '15 at 09:01
  • Lombok introduced `@FieldNameConstants` so there seem to be a [solution](https://stackoverflow.com/a/68684899/10950272) for it now. – makozaki Nov 04 '21 at 07:35

9 Answers9

27

Unfortunately, there is no way to do what you wish. Why?

In an era where we are still trying to prove that Java is not slow, storing metadata of where or how an object was constructed would have a large overhead, and since it has a very minimal range of use, it does not exist. Not only that: there are a bunch of technical reasons as well.

One reason is because of how the JVM is implemented. The specification for the Java class file format can be found here. It's quite a mouthful, but it is very informative.

As I said previously, an object can be constructed from anywhere, even situations where objects do not have names. Only objects defined as class members have names (for the obvious reason of access): objects constructed in methods do not. The JVM has a local variable table with a maximum of 65535 entries. These are loaded unto the stack and stored into the table via the *load and *store opcodes.

In other words, a class like

public class Test {
int class_member = 42;

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

gets compiled into

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--    
}

There, you can see a clear example from javap -v Test that while the name class_memberis preserved, the my_local_field has been abstracted to index 1 in the local variable table (0 is reserved for this).

This in itself would be a pain for debuggers and such, so a set of attributes (think metadata for class files) was designed. These include the LocalVariableTable, LineNumberTable, and LocalVariableTypeTable. These attributes are useful for stacktraces (LineNumberTable), and debuggers (LocalVariableTable and LocalVariableTypeTable). However, these are completely optional to include in files, and there is no guarantee that they are:

The LineNumberTable attribute is an optional variable-length attribute in the attributes table

The same holds for the rest of them.

Additionally, most compilers do not produce anything but the LineNumberTable by default, so you'd be out of luck even if it was possible.

Basically, it would be quite frustrating for developers to have a getFieldName(object) (which wouldn't be possible in the first place for local variables), and only have it work when those attributes are present.

So you are stuck for the foreseeable future with using getField with Strings. Shameless plug: IntelliJ seems to handle refactors with reflection quite well: having written Minecraft mods as well I know the feeling, and can say that work in refactoring is significantly reduces by IntelliJ. But the same is probably true for most modern IDEs: I'm sure the big players, Eclipse, Netbeans et al. have well-implemented refactoring systems in place.

Xyene
  • 2,304
  • 20
  • 36
  • See my answer for getting the name of the field. – Stefan May 30 '16 at 14:56
  • Why would replacing the name of the field to a string literal during compilation be "slow"? – Orestis P. Aug 12 '19 at 21:04
  • Not sure why it would be slow. C# has the keyword `nameof`, which is evaluated at compile time. It's quite useful to get reliable code usage with IntelliSense (so you can easily rename parameters and it will automatically update everywhere). https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/nameof – oscfri Oct 10 '22 at 12:15
16

If the field is private you can make it accessible:

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

To get field's name, go like that:

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

If you don't know the name of the field... well.. then what field would you like to get? :) You could get ALL of the fields using reflection and then loop through them.. But if you don't know the field, what is the actual sense and logic in this action?

user
  • 3,058
  • 23
  • 45
  • that's the problem, on input I would like linkTool not "linkTool". – monnef Feb 18 '13 at 20:50
  • Well, you don't get the name in quotes. What is the problem? – user Feb 18 '13 at 21:59
  • 6
    Code with heavy use of strings containing identifiers of fields/classes/methods/namespaces is not easy to maintain or refactor (it usually requires manual actions, IDEs have problems with this kind of things). – monnef Feb 19 '13 at 11:30
  • I tried code: Field field = this.getClass().getDeclaredField(field1); field.setAccessible(true); return field.getName(); in the POJO class itself but I am getting NullPointer. field1 is "private String field1" – Shashi Ranjan Aug 11 '16 at 04:37
  • 1
    Calling object.getClass().getDeclaredField(fieldName) defeats the purpose of the question which is to get the name of the field from the object itself. The parameter "fieldName" in this call will be the same as the value returned from (String)field.get(object). According to the question, "fieldName" is not known apriori. – Monte Creasor Oct 17 '17 at 01:19
16

How about using lombok annotation @FieldNameConstants

@FieldNameConstants
public class Mod {
    public ItemLinkTool linkTool;
}
...
String fieldName = Mod.Fields.linkTool; //fieldName == "linkTool"

With this feature we don't need to maintain code for getting field names (lombok does it for us). After deleting a field compiler will complain about it's usage.

Note that this feature is marked as experimental.

makozaki
  • 3,772
  • 4
  • 23
  • 47
10

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;
}
Stefan
  • 10,010
  • 7
  • 61
  • 117
7

This is only possible via reflection.

Use Class.getDeclaringFields() and java.reflection.Field.getName()

AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • 5
    Yes, but using reflection I need a field's name in string as a parameter (there is much more fields in the real class, also they have different types). My question was if there is a way of getting an instance of type java.lang.reflect.Field from just name of a field in code (NOT from a string). – monnef Feb 19 '13 at 12:20
  • yes with reflection, get all declaring fields, iterate over that array and the first that matches your file name is the right one. – AlexWien Feb 19 '13 at 12:28
  • how do I get a field name in string from field's name in code? look at the new example, maybe this time I wrote the question better. – monnef Feb 19 '13 at 12:53
  • @AlexWien there is no such method in Class.getDeclaringFields() , Answer is not understandable. – Shashi Ranjan Aug 11 '16 at 04:36
  • 2
    @ShashiRanjan Try *your-class-name*.class.getDeclaredFields(). This returns an array of fields of type java.reflection.Field which you can iterate over and apply the .getName() method to. – GHOST-34 Sep 07 '16 at 04:13
7

I think this is what you are looking for.

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;
}
Oguz
  • 1,867
  • 1
  • 17
  • 24
1

My motivation at getting the field name is using it to search for things in a key-value database (like mongodb). I have a struct with members (fields) that represents the saved data. I use the member names to search the db.

My suggestion is to put a static getter for each important member. Example:

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

Hopefully, Java will have some mechanism for this in the future, because nowadays, the metadata may be part of the data. Especially when doing stuff with JSON.

AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277
0

It can be done using runtime byte code instrumentation, for instance using Byte Buddy library, see this answer nameof equivalent in Java

jreznot
  • 2,694
  • 2
  • 35
  • 53
0

As well as Lombok's @FieldNameConstants it is possible to use pojo-analyzers which will also enable to access the getters and setters of the field (without reflection).

@DetailedPojo
public class Mod {
    public ItemLinkTool linkTool;
}
...
Mod mod = new Mod(myItemLinkTool);
DetailedMod.map.get("entityId").getFieldValue(mod); // myItemLinkTool
user7551211
  • 649
  • 1
  • 6
  • 25