如何通过Annotation访问JavaFX字段



我要做的事情:我有一个使用JavaFX的Java程序。我创建了一个fxml文件,在其中创建了JavaFx控制器,然后在AddEmployeeOrderController类中声明。我想将这些创建的控制器字段转移到POJO。由于我认为这非常耗时,会导致很多粗心的错误,所以我想更多地自动化这个过程。我的想法是为每个声明的JavaFX控制器字段做一个注释,这样,例如,只要按下另一个方法中的按钮,所有注释的字段都会自动检索。因此,你可以这样理解它,而不是自己手写所有内容,例如:

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

我首先形成了AddEmployeeOrderController,并声明了一些JavaFX字段并添加了一个注释。在AddEmployeeOrderController类中,我再次尝试访问带注释的字段。

然后,从逻辑上讲,我必须将java.lang.reflect.Field转换为JavaFXTextField,但这显然是不可能的。它只抛出IllegalArgumentException错误,当然,因为您不能将java.lang.reflect.Field强制转换为JavaFXTextField。

有没有一种方法可以在注释的帮助下实现我的想法,或者我被迫手工编写所有内容并生成所谓的样板代码。

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

您没有提供足够的(相关的)代码来理解您实际在做什么。然而,我们可以推断如下:

  1. 您正在使用的Field类是java.lang.reflect.Field

  2. 根据javadoc,Field.get(Object)应该通过引用一个实例来调用。。。或CCD_ 4。如果提供了一个实例,那么它需要是一个实例——声明字段的类,或者该类的子类。

  3. 如果使用非必需类的参数调用Field.get,则抛出IllegalArgumentException

所以。。。如果你告诉我们并向我们展示的是正确的。。。CCD_ 7不是要在该CCD_ 9对象上传递给CCD_ 8的正确对象。

您的意思是,您向我们展示的字段反射代码在AddEmployeeController类中。这意味着this将是AddEmployeeController实例。但是您正在迭代的Field实例是针对类AddEmployeeOrderController声明的字段的。因此,应该使用引用AddEmployeeOrderController实例的(非null)值调用get。(或者你应该迭代AddEmployeeController的声明字段。如果没有更多的上下文,很难说出正确的修复方法。)


如果我们去掉所有动态/反射的东西,你的代码所做的与这个非反射代码类似:

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

它不会编译,因为AddEmployeeController没有一个名为someField的字段。

你实际上需要做的是类似的事情:

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

只是澄清一下,问题不是来自中的类型转换

(TextField) field.get(this);

如果类型转换失败,那么异常将是ClassCastException。您看到的异常来自get调用!

问题不在于注释。


跟进

我已经接受了您的(尝试的)MRE,考虑了JavaFX的内容,并将其转化为工作代码。我的版本展示了如何通过匹配字段的类型和注释来反射式地提取字段值。

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

请注意,这是REAL代码。它编译、运行和。。。作品试试看。如果你把String改成(比如)TextField,你就能适应你的问题。(字段的实际类型几乎与核心问题完全无关。正如你所看到的。)

(有一点可以改进和简化,那就是类型测试应该使用Class.equals,而不是测试类的名称。它更干净、更可靠。)

你担心的一个问题是(我认为)你需要大量的样板代码来解决你的问题。我不认为是这样:

  1. 如果我为MyTest声明一个抽象超类,并将doIt()的实现放在那里它应该只是工作。请注意,doIt()使用this.getClass()来获取当前对象的类。

  2. 如果doIt()在一个不相关的类中,只要该方法有一个用于传递目标Object的参数,它也会起作用。

  3. 甚至可以根据字段的类型将其参数化。或者查找属于给定类型的子类型的字段。等等。(提示:您需要将类型作为Class对象传递。)


我说";(尝试)MRE";这是有原因的。事实上,您的代码不会编译。实际上,您有一个未声明的变量(object),其预期类型和用途很难理解。我认为这是一个错误,并猜测您的意图是在那里使用field但我不应该猜测

真正的MRE需要是完整的和可编译的(除非你的问题是如何编译它)。理想情况下,它也应该是可运行的,并且运行MRE应该重现您所询问的问题。

关键是,我们(试图帮助您的人)需要确信我们了解您的问题是什么。这就是MRE的目的

最新更新