如何通过注释访问 JavaFX 字段?
How to acces JavaFX fields via Annotation?
我正在尝试做的事情:
我有一个 Java 程序,我在其中使用了 JavaFX。我创建了一个 fxml 文件,我在其中创建了 JavaFx 控制器,然后我在 AddEmployeeOrderController class 中声明了它。我想将这些创建的控制器字段传输到 POJO。因为我认为这非常耗时并且会导致很多粗心的错误,所以我想使这个过程更加自动化。我的想法是为每个声明的 JavaFX 控制器字段做一个注释,这样,例如,在另一种方法中按下按钮时,将自动检索所有带注释的字段。所以你可以这样理解它而不是自己手写所有东西,例如:
EmployeeDto employeeDto = new EmployeeDto(textfield.getText(), textfield2.getText()..);
我首先构建了 AddEmployeeOrderController 并声明了一些 JavaFX 字段并添加了注释。再次在 AddEmployeeOrderController class 中,我尝试访问带注释的字段。
然后,从逻辑上讲,我必须将 java.lang.reflect.Field 转换为 JavaFX TextField,但这显然是不可能的。它只会抛出 IllegalArgumentException 错误,当然是因为您不能将 java.lang.reflect.Field 转换为 JavaFX TextField。
有没有一种方法可以借助注释来实现我的想法,或者我是否被迫手动编写所有内容并生成所谓的样板代码。
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 {
}
您没有提供足够的(相关的)代码来理解您实际在做什么。但是,我们可以推断出以下内容:
您使用的Field
class是java.lang.reflect.Field
.
根据 javadoc,Field.get(Object)
应该通过对实例的引用来调用...或 null
。如果提供了一个实例,那么它需要是声明该字段的 class 实例,或者 class.
的子 class
如果调用 Field.get
时使用的参数不是必需的 class,则会抛出 IllegalArgumentException
。
所以...如果您告诉我们并向我们展示的内容是正确的...this
不是传递给 Field.get
的正确对象 Field
对象.
您是说您向我们展示的场反射代码在 AddEmployeeController
class 中。这意味着 this
将是一个 AddEmployeeController
实例。但是您正在迭代的 Field
个实例是针对 class AddEmployeeOrderController
声明的字段。因此,您 应该 使用引用 AddEmployeeOrderController
实例的(非空)值调用 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();
}
}
请注意,这是真实代码。它编译、运行和……工作。尝试一下。如果将 String
更改为(例如)TextField
,则可以根据您的问题进行调整。 (字段的实际类型几乎与核心问题完全无关。如您所见。)
(可以改进和简化的一件事是类型测试应该使用 Class.equals
而不是测试 class 的名称。它更干净,更可靠。)
您担心的一个问题是(我认为)您需要大量样板代码才能解决您的问题。我不认为是这样:
如果我为 MyTest
声明一个抽象超级 class 并将 doIt()
的实现放在那里...... 它应该可以正常工作。请注意 doIt()
使用 this.getClass()
获取当前对象的 class.
如果 doIt()
在不相关的 class 中,它也可以工作,前提是该方法有一个用于传递目标对象的参数。
甚至可以根据字段类型对其进行参数化。或者查找作为给定类型子类型的字段。等等。 (提示:您需要将类型作为 Class
对象传递。)
我说“(尝试)MRE”是有原因的。事实上,您的代码无法编译。实际上,您有一个未声明的变量 (object
),其预期类型和用途很难理解。我认为这是一个错误,并猜测您的意图是在那里使用 field
。 但我不应该猜测!
A real MRE 需要完整且可编译(除非你的问题是如何让它编译)。理想情况下,它也应该是可运行的,并且 运行 MRE 应该重现您所询问的问题。
重点是我们(试图帮助您的人)需要确定我们理解什么您的问题是什么。这就是 MRE 的目的。
我正在尝试做的事情: 我有一个 Java 程序,我在其中使用了 JavaFX。我创建了一个 fxml 文件,我在其中创建了 JavaFx 控制器,然后我在 AddEmployeeOrderController class 中声明了它。我想将这些创建的控制器字段传输到 POJO。因为我认为这非常耗时并且会导致很多粗心的错误,所以我想使这个过程更加自动化。我的想法是为每个声明的 JavaFX 控制器字段做一个注释,这样,例如,在另一种方法中按下按钮时,将自动检索所有带注释的字段。所以你可以这样理解它而不是自己手写所有东西,例如:
EmployeeDto employeeDto = new EmployeeDto(textfield.getText(), textfield2.getText()..);
我首先构建了 AddEmployeeOrderController 并声明了一些 JavaFX 字段并添加了注释。再次在 AddEmployeeOrderController class 中,我尝试访问带注释的字段。
然后,从逻辑上讲,我必须将 java.lang.reflect.Field 转换为 JavaFX TextField,但这显然是不可能的。它只会抛出 IllegalArgumentException 错误,当然是因为您不能将 java.lang.reflect.Field 转换为 JavaFX TextField。
有没有一种方法可以借助注释来实现我的想法,或者我是否被迫手动编写所有内容并生成所谓的样板代码。
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 {
}
您没有提供足够的(相关的)代码来理解您实际在做什么。但是,我们可以推断出以下内容:
您使用的
Field
class是java.lang.reflect.Field
.根据 javadoc,
的子 classField.get(Object)
应该通过对实例的引用来调用...或null
。如果提供了一个实例,那么它需要是声明该字段的 class 实例,或者 class.如果调用
Field.get
时使用的参数不是必需的 class,则会抛出IllegalArgumentException
。
所以...如果您告诉我们并向我们展示的内容是正确的...this
不是传递给 Field.get
的正确对象 Field
对象.
您是说您向我们展示的场反射代码在 AddEmployeeController
class 中。这意味着 this
将是一个 AddEmployeeController
实例。但是您正在迭代的 Field
个实例是针对 class AddEmployeeOrderController
声明的字段。因此,您 应该 使用引用 AddEmployeeOrderController
实例的(非空)值调用 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();
}
}
请注意,这是真实代码。它编译、运行和……工作。尝试一下。如果将 String
更改为(例如)TextField
,则可以根据您的问题进行调整。 (字段的实际类型几乎与核心问题完全无关。如您所见。)
(可以改进和简化的一件事是类型测试应该使用 Class.equals
而不是测试 class 的名称。它更干净,更可靠。)
您担心的一个问题是(我认为)您需要大量样板代码才能解决您的问题。我不认为是这样:
如果我为
MyTest
声明一个抽象超级 class 并将doIt()
的实现放在那里...... 它应该可以正常工作。请注意doIt()
使用this.getClass()
获取当前对象的 class.如果
doIt()
在不相关的 class 中,它也可以工作,前提是该方法有一个用于传递目标对象的参数。甚至可以根据字段类型对其进行参数化。或者查找作为给定类型子类型的字段。等等。 (提示:您需要将类型作为
Class
对象传递。)
我说“(尝试)MRE”是有原因的。事实上,您的代码无法编译。实际上,您有一个未声明的变量 (object
),其预期类型和用途很难理解。我认为这是一个错误,并猜测您的意图是在那里使用 field
。 但我不应该猜测!
A real MRE 需要完整且可编译(除非你的问题是如何让它编译)。理想情况下,它也应该是可运行的,并且 运行 MRE 应该重现您所询问的问题。
重点是我们(试图帮助您的人)需要确定我们理解什么您的问题是什么。这就是 MRE 的目的。