javaFX 8中@NamedArg注解的目的是什么?
我会知道JavaFX 8中@NamedArg注解的用例是什么
javadoc并没有给我们更多的细节,Javadoc:提供关于参数名称的信息的注释。
没有更多的信息,文件,在互联网上的例子。
也许有人可以帮忙?
问候。
@NamedArg
注解允许FXMLLoader
实例化一个没有零参数构造函数的类。
技术背景:
FXMLLoader
使用reflectionFXMLLoader
创build对象。 通常情况下,如果您使用与不带参数的构造函数相对应的类的标签,则通过调用Class.newInstance()
来创build一个对象,该类调用无Class.newInstance()
构造函数。
如果一个类只能用带参数的构造函数来定义,那么这是有问题的。 主要的问题是Java语言规范不要求在运行时保留参数名(方法或构造函数)。 这意味着FXMLLoader
不能确定哪个参数具有给定的名称。
为了使这个具体,假设我们定义一个Person
类如下:
package application; import javafx.beans.NamedArg; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class Person { private final StringProperty firstName ; private final StringProperty lastName ; public Person(String firstName, String lastName) { this.firstName = new SimpleStringProperty(this, "firstName", firstName); this.lastName = new SimpleStringProperty(this, "lastName", lastName); } // methods.... }
在FXML中,我们可能会尝试创build一个Person
,如下所示:
<Person firstName="Jacob" lastName="Smith"/>
这是行不通的,因为FXML加载器不能保证Person
类的运行时表示保留关于哪个构造器参数是firstName
和哪个是lastName
。
历史背景
Java 2.2定义了与每个控件对应的“Builder”类。 这些构build器类遵循标准构build器模式。 当FXMLLoader
遇到引用一个没有零参数构造函数的类的标签时,它将使用相应的构build器来创build实例。
不幸的是,构build器类的实现是有缺陷的, 它们在JavaFX 8中被弃用 ,并且会在更高版本(可能是JavaFX 9)中被删除。 这就给FXMLLoader
留下了一个问题, FXMLLoader
不再需要构build器类来实例化没有零参数构造器的类。 一个真正的例子是Color
类,它没有零参数的构造函数,并将其构build器类删除。
@NamedArgs
解决这个问题的方法是引入一个注释,用于在运行时保留一个方法(或构造函数)参数的名称。 通过reflection,我们可以查询构造函数/方法的参数列表,并获取每个参数的types(而不是名称)。 也可以查询每个参数的任何注释,并获得这些注释的值。 所以@NamedArg
注解是专门为了在运行时保留一个参数名而引入的。
例
例如,使用我们上面介绍的Person
类:
package application; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class Person { private final StringProperty firstName ; private final StringProperty lastName ; public Person(String firstName, String lastName) { this.firstName = new SimpleStringProperty(this, "firstName", firstName); this.lastName = new SimpleStringProperty(this, "lastName", lastName); } public final StringProperty firstNameProperty() { return firstName; } public final String getFirstName() { return firstNameProperty().get(); } public final void setFirstName(final String firstName) { firstNameProperty().set(firstName); } public final StringProperty lastNameProperty() { return lastName; } public final String getLastName() { return lastNameProperty().get(); } public final void setLastName(final String lastName) { lastNameProperty().set(lastName); } }
如果您尝试使用FXML加载这个:
Person.fxml:
<?xml version="1.0" encoding="UTF-8"?> <?import application.Person?> <Person firstName="Jacob" lastName="Smith" xmlns:fx="http://javafx.com/fxml/1" />
Main.java:
package application; import java.io.IOException; import javafx.fxml.FXMLLoader; public class Main { public static void main(String[] args) throws IOException { Person person = FXMLLoader.load(Main.class.getResource("Person.fxml")); System.out.println(person.getFirstName()+" "+person.getLastName()); } }
那么你在运行时会看到一个错误:
Caused by: java.lang.NoSuchMethodException: application.Person.<init>()
表明FXMLLoader
正在寻找一个不带参数的构造函数( Person.<init>()
)。
在JavaFX 8中,您可以通过使用@NamedArg
批注指定参数的名称来解决问题:
package application; import javafx.beans.NamedArg; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class Person { private final StringProperty firstName ; private final StringProperty lastName ; public Person(@NamedArg("firstName") String firstName, @NamedArg("lastName") String lastName) { this.firstName = new SimpleStringProperty(this, "firstName", firstName); this.lastName = new SimpleStringProperty(this, "lastName", lastName); } public final StringProperty firstNameProperty() { return firstName; } public final String getFirstName() { return firstNameProperty().get(); } public final void setFirstName(final String firstName) { firstNameProperty().set(firstName); } public final StringProperty lastNameProperty() { return lastName; } public final String getLastName() { return lastNameProperty().get(); } public final void setLastName(final String lastName) { lastNameProperty().set(lastName); } }
这将允许FXMLLoader
根据需要加载类。
请注意,您也可以通过定义构build器类来解决该问题,并且这也适用于JavaFX 2.0及更高版本。 JavaFX团队决定(可能是正确的),使用这种方法的方式不会受到构build器初始实现中存在的错误的影响,这会增加框架代码库的膨胀。
package application; public class PersonBuilder { private String firstName ; private String lastName ; private PersonBuilder() { } public static PersonBuilder create() { return new PersonBuilder(); } public PersonBuilder firstName(String firstName) { this.firstName = firstName ; return this ; } public PersonBuilder lastName(String lastName) { this.lastName = lastName ; return this ; } public Person build() { return new Person(firstName, lastName); } }
很显然,如果您使用的是JavaFX 8,构造函数注释方法的工作量要less得多。
参考文献:
- build议贬低build设者
- 调整请求以添加构造函数注释
- build造者模式
- FXML文档 (讨论构build者,但不是
@NamedArg
) - 请求将@NamedArgs上的文档添加到“FXML简介”文档中