-1

What I'm trying to do: I have a Java program in which I use JavaFX. I created a fxml file in which I created JavaFx controllers, which I then declared in the AddEmployeeOrderController class. I would like to transfer these created controller fields to a POJO. Since I think it is very time-consuming and leads to a lot of careless mistakes, I wanted to automate this process more. My idea was to make an annotation for each declared JavaFX controller field so that, for example, all annotated fields are gonna be retrieved automatically at for example a push of a button in another method. So you can understand it that way instead of writing everything by hand yourself, e.g.:

EmployeeDto employeeDto = new EmployeeDto(textfield.getText(), textfield2.getText()..);

I first formed the AddEmployeeOrderController and declared some JavaFX field and added an annotation. Again in the AddEmployeeOrderController class I tried to access the annotated field.

Then, logically, I would have to cast to cast the java.lang.reflect.Field to a JavaFX TextField, but that is obviously not possible. It throws only IllegalArgumentException errors, and of course because you can't cast a java.lang.reflect.Field to a JavaFX TextField.

Is there a way in which my idea can be achieved with the help of annotation, or am I forced to write everything by hand and generate so-called boilerplate code.

 public class AddEmployeeOrderController implements Initializale {
     @FXML
     @MyAnno
     public TextField orderDateFromTextField;

     public void atPushButton() {
         for (Field field : AddEmployeeOrderController.class.getDeclaredFields()) {
             if (field.isAnnotationPresent(MyAnno.class)) {
                 if (((Field) object).getType().getCanonicalName()
                                               .contains("TextField")) {
                     TextField textField = (TextField) field.get(this);
                     textField.getText();
                 }
             }
         }
     }
 }

 @Retention(RetentionPolicy.RUNTIME)
     public @interface MyAnno {
 }
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
Koala
  • 29
  • 7
  • 2
    Could you explain what your coal actually is? What do you want to achieve with this mechanism? – mipa Mar 15 '21 at 13:31
  • 1
    Start here -> https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx – SedJ601 Mar 15 '21 at 18:09
  • Why are you doing `Object object = field;` if all you do is immediately cast that back to a `Field`? Just use the `Field` reference. And if you want to get the values of the fields from the controller instance then you need to pass the controller instance to `Field#get(...)`. – Slaw Mar 15 '21 at 20:36
  • yeah, 'Object object = field'; is obsolete, some leftover from my previous attempts. But as I understand you right, 'Textield textfield = (TextField) field.get(orderDateFromTextField)' will do it? Because I dont think so, it will generate the same exception. – Koala Mar 15 '21 at 21:23
  • No. `AddEmployeeOrderController controller = ...;`. Now you have an instance _of the controller class_. You get the `Field` from the class of the instance and then you call `Object value = theField.get(controller)`. But make sure the instance referenced by `controller` (or whatever field/variable you use) is the same instance as used by your UI. – Slaw Mar 16 '21 at 03:36
  • I am sorry, I am initiating the for loop method in the same **AddEmployeeOrderController** method. Creating an instance of the class where the TextField is placed an then, as you said, calling it in `Object value = theField.get(controller.myTextField)` works fine. But that stiil does no solve my problem. I would have to cast it to TextField in order to obtain the input text from that field via getText(). – Koala Mar 16 '21 at 09:57
  • How could `theField.get(controller.myTextField)` work fine for you? You are not getting a field from the `TextField` class and so passing an instance of `TextField` to `get` should throw an exception. – Slaw Mar 16 '21 at 18:50

1 Answers1

1

You have not provided sufficient (relevant) code to understand what you are actually doing. However, we can deduce the following:

  1. The Field class you are using is java.lang.reflect.Field.

  2. According to the javadoc, the Field.get(Object) should be called with a reference to an instance ... or null. If an instance is provided, then it needs to be an instance the class that declares the field, or a subclass of that class.

  3. If Field.get is called with a parameter that is NOT of the required class, IllegalArgumentException is thrown.

So ... if what you have told us and shown us is correct ... this is not the correct object to be passing to Field.get on that Field object.

You are saying that the field reflection code you are showing us is in the AddEmployeeController class. That means that this would be a AddEmployeeController instance. But the Field instances you are iterating are for the fields declared by the class AddEmployeeOrderController. So, you should be calling get with a (non-null) value that refers to an AddEmployeeOrderController instance. (Or maybe you should be iterating the declared fields of AddEmployeeController. Without more context it is difficult to say what the correct fix is.)


If we strip away all of the dynamic / reflective stuff, what your code is doing is analogous to this non-reflective code:

public class AddEmployeeOrderController {
    public TextField someField; 
}

public class AddEmployeeController {
    public void someMethod() {
        TextField t = (TextField)(this.someField);
    }
}

It won't compile because AddEmployeeController doesn't have a field called someField.

What you actually need to do is the analog of this:

public class AddEmployeeController {
    public void someMethod(AddEmployeeOrderController aeoc) {
        TextField t = (TextField)(aeoc.someField);
    }
}   

Just to clarify, the problem is not coming from the typecast in

(TextField) field.get(this);

If the typecast was failing, then the exception would be a ClassCastException. The exception you are seeing comes from the get call!

And the problem is not due to annotations.


FOLLOW-UP

I have taken your (attempted) MRE, factored out the JavaFX stuff, and turned it into working code. My version shows how to extract a field value reflectively by matching the field's type and an annotation.

package test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
}

// End of MyAnno

package test;
import java.lang.reflect.Field;

public class MyTest {
    @MyAnno
    public String someField = "Hi Mom";
    
    public void doIt() throws Exception {
        for (Field field : this.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(MyAnno.class)) {
                if (field.getType().getCanonicalName().equals("java.lang.String")) {
                    String value = (String) field.get(this);
                    System.out.println(value);
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        new MyTest().doIt();
    }
}

Note that this is REAL code. It compiles, runs and ... works. Try it. If you change String to (say) TextField, you can adapt it to your problem. (The actual type of the field is almost entirely irrelevant to the core problem. As you can see.)

(One thing that could be improved and simplified is that the type test should use Class.equals rather than testing the name of the class. It is cleaner and more reliable.)

One of your concerns was (I think) that you would need a lot of boilerplate code to make this work for your problem. I don't think that is the case:

  1. If I declare an abstract superclass for MyTest and put the implementation of doIt() there ... it should just work. Notice that doIt() uses this.getClass() to get the current object's class.

  2. It would also work if doIt() was in an unrelated class, provided that the method had an argument for passing the target Object.

  3. It would even be possible to parameterize this on the type of the field. Or look for fields that are subtypes of a given type. And so on. (Hint: you would need to pass the type as a Class object.)


I said "(attempted) MRE" for a reason. In fact, your code doesn't compile. Indeed, you have a variable (object) which is not declared, and whose intended type and purpose is hard to fathom. I have assumed that it was a mistake, and guessed that your intention was to use field there. But I should not have to guess!

A real MRE needs to be complete and compilable (unless your problem is how to get it to compile). Ideally it should also be runnable, and running the MRE should reproduce the problem you are asking about.

The point is that we (people trying to help you) need to be sure that we understand what your problem is. That is the purpose of the MRE.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Yes, everything you say is correct, only my question and also my goal is, as I wrote it: to pass the TextField via annotation and then to do, for example, getText () on the via annotation passed TextField, the exactly same which I declared in AssEmployeeController class. As you say, and as I also said, is it not possible to cast java.lang.reflect.Field onto a TextField JavaFX ..? So what would be another possibility, whereby I absolutely have to make use of annotations! – Koala Mar 15 '21 at 14:21
  • 1
    If my updated answer still doesn't make sense to you, please update the question with a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example), so that we can see *exactly* what you are doing. – Stephen C Mar 15 '21 at 15:01
  • Thanks again for your interest, I've rewritten my question, hope that's not a problem. Believe now should be what I'm trying to achieve. – Koala Mar 15 '21 at 16:21
  • 1
    Yes. I see you have done that. I believe that it doesn't invalidate anything I have written in my answer, and ... indeed ... that my answer would help you solve my problem if you understood it. However, I think that the only way we will be able to *convince* you is if you write a minimal reproducible example. – Stephen C Mar 15 '21 at 22:30
  • @Koala read the referenced help page and act accordingly – kleopatra Mar 15 '21 at 23:18
  • @Stephen C **AddEmployeeController** was a typo, of course I meant all the time **AddEmployeeOrderController**. The example in your answer is understandable, but does not answer my question. My question was, how to do it with Annotations, because I will not feel compelled to write by hand every field into into a method. So far I have stuck with the fact that I cannot cast Field to Textield. – Koala Mar 16 '21 at 10:10
  • I think we are going to stay stuck then ... unless you put in the effort, and write us a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Stephen C Mar 16 '21 at 10:15
  • @StephenC okay, undestand, I did it, hope it is okay now! – Koala Mar 16 '21 at 10:51
  • Is my question going to be reopened again? – Koala Mar 16 '21 at 11:16
  • It depends. I will vote. But you took such a long time (20 hours) to add the MRE that the question may have dropped off the radar. Lesson: if you want help ... listen, don't argue. – Stephen C Mar 16 '21 at 11:26
  • I saw your edit and you are right with listening not arguing. Thanks anyway, I 've got a lesson but I've learned a lot. – Koala Mar 16 '21 at 14:35