3

I am having a declared field in RequestDto as (requestDto has many declared field but i want only this custom field)

private String custom; 

i wanted to get this declared field name Custom as String

like String name-"custom"(but custom has to be dynamic)

The solution has to be without modifying default getters and setters

Actually i have a problem like when a requestDto is processed for content validation if one of the field in the request Dto is having an bad data i should provide an error message along with the field name as string which had an irrelevant data

The problem is when custom field is modified in RequestDto in future ,the person modifying it will not even be knowing that custom field is set as string and it is used ,so i wanted to have the custom field name as string from RequestDto only ,so when ever the field value is changed i get the changed name in string

Lokeshkumar R
  • 575
  • 5
  • 13
  • 3
    What do you mean by "dynamic"? You must just have the name in a variable at runtime – ernest_k Nov 29 '18 at 11:19
  • "get specific declared field", "i want only this custom field" - How do you want to specify *which* field you want to access? As you said, there may be many fields in a class. What makes this field different from the others so that you could locate the specific field of interest? - Commonly, annotations are used for this. A field is specially annotated and at runtime, via reflection, all fields are searched for the one wich has the specific annotation on it. – JimmyB Nov 29 '18 at 11:29
  • In other words, you can access all the [`Field`s](https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Field.html) via reflection. The `Field` class provides all the information you can get about the field. Some of this information must be used if you are to find a field whose name you don't know. It could be the type, a name (pattern), an annotation, the actual declaring class or something like that. – JimmyB Nov 29 '18 at 11:34
  • Actually i have a problem like when a requestDto is processed for content validation if one of the field in the request Dto is having an bad data i should provide an error message along with the field name as string which had an irrelevant data – Lokeshkumar R Nov 29 '18 at 11:35
  • @JimmyB why do you link the Java 7 API? although that part is probably still the same, you might rather link newer versions (at least 8 as also 8 is stated as tag)... – Roland Nov 29 '18 at 11:38
  • @LokeshkumarR how do you know that it has "bad data"? – Roland Nov 29 '18 at 11:38
  • @Roland Because it's sill the first google result for me and because Java7/8/9 does not make a difference for the OP. – JimmyB Nov 29 '18 at 11:51
  • @LokeshkumarR So how do you access and validate the data? Somewhere along the access path there must be the information which field is being validated. – JimmyB Nov 29 '18 at 11:52
  • 2
    Your problem description lacks the critical part of information on how you get to the conclusion that a certain field is 'of interest'. The answer to your question fully depends on that 'detail'. – JimmyB Nov 29 '18 at 12:11
  • @JimmyB yes you are right I will actually know which field I am validating , if I am validating the custom field in request dto , If it fails on content validation , then I can pass error field value as custom , but what if some on after 2 months try to change the field name in request dto from custom to customStatus then eclipse refactor will not refactor this condition ryt , the user who refactors will not even knew that there was case handled based on the field name ryt – Lokeshkumar R Nov 29 '18 at 12:12
  • @JimmyB we do not yet know whether 7/8/9 makes a difference to the OP... but we know it is at least (and probably only) Java 8 that is being used... that googling returned the wrong javadoc page for you is not at all a good excuse ;-) – Roland Nov 29 '18 at 12:12
  • @LokeshkumarR So you did implement the validation yourself, calling known getters and validating the return value? - Else, how do you know which field should be validated how? Are you trying something like `if (getSomeField() != EXPECTED_VALUE) throw new Exception("getSomeField() returned wrong value.);`? – JimmyB Nov 29 '18 at 12:16
  • @JimmyB yes correct that is how I do validation on fields ,each field has different validation ,and custom exception is thrown – Lokeshkumar R Nov 29 '18 at 12:18
  • Look into using a validation framework (see https://www.baeldung.com/javax-validation). – JimmyB Nov 29 '18 at 13:20
  • Related: https://stackoverflow.com/questions/19845213/how-to-get-the-methodinfo-of-a-java-8-method-reference – JimmyB Nov 29 '18 at 13:34

2 Answers2

4

Edit: after you have added the most important piece of information in the comment "what if someone changes that name in the future"... well... then reflection is not the way to go!

I would then rather use something like a Function<RequestDto, String> or similar instead, e.g.:

static String getCustom(RequestDto requestDto, Function<RequestDto, String> customExtractor) {
  return customExtractor.apply(requestDto);
}

and call it via:

RequestDto someRequest = ...;
getCustom(someRequest, RequestDto::getCustom);

If that field is refactored in future that part will be refactored too then automatically (well... depends on your IDE probably ;-)). But... it will not give you the appropriate field name as error message. Note that there isn't a nice way to catch the right field, if the only property to get it, is its name. If you would put an annotation to the field, you could then filter and grab the field with that specific annotation. That way you do not need to know the actual name... but counterquestion: how can you be sure that only 1 field is annoted with that annotation? I would rather redesign that specific part of your application. I wouldn't expose any field to the "client". If that means that I have potentially different names in the error message and the field names, so be it.

Note also that modern IDEs allow you to refactor strings as well (when you rename a field). You just need to ensure that this information is spread across the team then (and also future members).


Previous answer using reflection:

If with dynamic you mean, that it must start with that name or contain that name, you may want to use something as follows, i.e. getDeclaredFields() and iterate over them to filter out interested fields:

private static String getCustom(RequestDto requestDto) throws NoSuchFieldException, IllegalAccessException {
    return Arrays.stream(requestDto.getClass().getDeclaredFields())
            .filter(e -> e.getName().startsWith("custom"))
            .findFirst() // if the first match suffices
            .map(f -> {  // you may want to extract that part if you use streams
                f.setAccessible(true);
                try {
                    return (String) f.get(requestDto);
                } catch (IllegalAccessException e) {
                    // TODO whatever you need to do...
                    return null;
                }
            }).orElseThrow(IllegalArgumentException::new); // or whatever suites better
}

This sample should suffice in case you need to filter by type or by annotation, etc. Just adapt the filter then accordingly.

If it's just the name of a field that you require, it is Field.getName() what you want.

Roland
  • 22,259
  • 4
  • 57
  • 84
2
 private static String getCustom(RequestDto requestDto) throws NoSuchFieldException, IllegalAccessException {
    Field field = requestDto.getClass().getDeclaredField("custom");
    field.setAccessible(true);
    return (String) field.get(requestDto);
}

If it's not "dynamic enough" for you:

private static String getField(RequestDto requestDto, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = requestDto.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return (String) field.get(requestDto);
    }
Ricola
  • 2,621
  • 12
  • 22