javaFX 8 中@NamedArg 注释的目的是什么? [英] What is the purpose of @NamedArg annotation in javaFX 8?

查看:44
本文介绍了javaFX 8 中@NamedArg 注释的目的是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道@NamedArg注解在JavaFX 8中的用例是什么

javadoc 没有给我们更多的细节,Javadoc :提供有关参数名称信息的注释.

互联网上没有更多信息、文档和示例.

也许有人可以帮忙?

问候.

解决方案

@NamedArg 注释允许 FXMLLoader 实例化一个没有零参数的类构造函数.

技术背景:

FXMLLoader 使用反射创建对象.通常,如果您使用与具有不带参数构造函数的类相对应的标记,则会通过调用 Class.newInstance() 从该类创建一个对象,该类调用无参数构造函数.>

如果一个类只用带参数的构造函数定义,那么这是有问题的.主要问题是 Java 语言规范不要求在运行时保留参数(方法或构造函数)的名称.这意味着 FXMLLoader 没有直接的、有保证的方法来确定哪个参数具有给定的名称.

为了具体化,假设我们定义了一个 Person 类,如下所示:

打包应用程序;导入 javafx.beans.NamedArg;导入 javafx.beans.property.SimpleStringProperty;导入 javafx.beans.property.StringProperty;公共类人{私有最终 StringProperty firstName ;私有最终 StringProperty lastName ;公共人(字符串名字,字符串姓氏){this.firstName = new SimpleStringProperty(this, "firstName", firstName);this.lastName = new SimpleStringProperty(this, "lastName", lastName);}//方法....}

在 FXML 中,我们可能会尝试创建一个 Person 如下:

这行不通,因为 FXML 加载器不能保证 Person 类的运行时表示会保留关于哪个构造函数参数是 firstName 以及哪个是firstName 的信息.是 lastName.

历史背景

Java 2.2 定义了对应于每个控件的Builder"类.这些构建器类遵循标准构建器模式.当 FXMLLoader 遇到引用没有零参数构造函数的类的标记时,它会使用相应的构建器来创建实例.

不幸的是,构建器类的实现存在缺陷,它们已被弃用在 JavaFX 8 中,并将在更高版本(可能是 JavaFX 9)中删除.这给 FXMLLoader 留下了一个问题,它不再需要构建器类来实例化没有零参数构造函数的类.一个真实的例子是 Color 类,它没有零参数构造函数,并将删除其构建器类.

@NamedArgs

对此的修复是引入一个注释,用于在运行时保留方法(或构造函数)参数的名称.通过反射,我们可以查询一个构造函数/方法的参数列表,并得到每个参数的类型(而不是名称).也可以查询任何注释的每个参数,并获取这些注释的值.所以 @NamedArg 注释是专门为了在运行时保留参数名称而引入的.

示例

以我们上面介绍的Person类为例:

打包应用程序;导入 javafx.beans.property.SimpleStringProperty;导入 javafx.beans.property.StringProperty;公共类人{私有最终 StringProperty firstName ;私有最终 StringProperty 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:

Main.java:

打包应用程序;导入 java.io.IOException;导入 javafx.fxml.FXMLLoader;公共课主要{public static void main(String[] args) 抛出 IOException {Person person = FXMLLoader.load(Main.class.getResource("Person.fxml"));System.out.println(person.getFirstName()+" "+person.getLastName());}}

然后你在运行时看到一个错误:

Caused by: java.lang.NoSuchMethodException: application.Person.()

表明 FXMLLoader 正在寻找一个不带参数的构造函数 (Person.()).

在 JavaFX 8 中,您可以通过使用 @NamedArg 批注指定参数名称来解决此问题:

打包应用程序;导入 javafx.beans.NamedArg;导入 javafx.beans.property.SimpleStringProperty;导入 javafx.beans.property.StringProperty;公共类人{私有最终 StringProperty firstName ;私有最终 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 根据需要加载类.

请注意,您还可以通过定义构建器类来解决该问题,并且这也适用于 JavaFX 2.0 及更高版本.JavaFX 团队决定(可能是正确的)以一种不受构建器初始实现中存在的错误影响的方式使用这种方法会给框架的代码库增加过多的膨胀.

打包应用程序;公共类 PersonBuilder {私人字符串名字;私人字符串姓氏;私人 PersonBuilder() { }公共静态 PersonBuilder 创建(){返回新的 PersonBuilder();}public PersonBuilder firstName(String firstName) {this.firstName = firstName ;返回这个;}公共 PersonBuilder 姓氏(字符串姓氏){this.lastName = 姓氏;返回这个;}公共人构建(){返回新人(名字,姓氏);}}

显然,如果您使用的是 JavaFX 8,则构造函数注释方法的工作量要少得多.

参考:

I would like to know what is the use case of @NamedArg annotation in JavaFX 8

The javadoc does not give us more details, Javadoc : Annotation that provides information about argument's name.

And no more information, documentation, examples on the internet.

Maybe someone could help ?

Regards.

解决方案

The @NamedArg annotation allows an FXMLLoader to instantiate a class that does not have a zero-argument constructor.

Technical Background:

The FXMLLoader creates objects using reflection. Typically, if you use a tag corresponding to a class with a constructor taking no arguments, an object is created from that class by calling Class.newInstance(), which invokes the no-argument constructor.

If a class is defined only with constructors that take parameters, then this is problematic. The main issue is that the Java Language Specification does not require the names of parameters (to methods or constructors) to be retained at runtime. This means there's no direct, guaranteed, way for the FXMLLoader to determine which parameter has a given name.

To make this concrete, suppose we define a Person class as follows:

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....

}

In FXML we might try to create a Person as follows:

<Person firstName="Jacob" lastName="Smith"/>

This won't work, because the FXML loader has no guarantee that the runtime representation of the Person class retains the information as to which constructor parameter is firstName and which is lastName.

Historical background

Java 2.2 defined "Builder" classes corresponding to each control. These builder classes follow the standard builder pattern. When the FXMLLoader encounters a tag referencing a class with no zero-argument constructor, it would use the corresponding builder to create the instance.

Unfortunately, the implementation of the builder classes was flawed, and they were deprecated in JavaFX 8, and will be removed in a later version (probably JavaFX 9). This left a problem for the FXMLLoader, which would no longer have builder classes to rely on for instantiating classes with no zero-argument constructor. A real example is the Color class, which has no zero-argument constructor and will have its builder class removed.

@NamedArgs

The fix for this was to introduce an annotation that is used to retain a name of a method (or constructor) argument at runtime. By reflection, we can query the parameter list of a constructor/method, and get the type (but not the name) of each parameter. It is also possible to query each parameter for any annotations, and get the value of those annotations. So the @NamedArg annotation was introduced specifically for the purpose of retaining a name of a parameter at runtime.

Example

For an example, use the Person class we introduced above:

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

If you try to load this using 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());
    }
}

then you see an error at runtime:

Caused by: java.lang.NoSuchMethodException: application.Person.<init>()

indicating the FXMLLoader is looking for a constructor taking no arguments (Person.<init>()).

In JavaFX 8, you can fix the problem by specifying the name of the parameters with the @NamedArg annotation:

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

This will allow the FXMLLoader to load the class as required.

Note that you can also fix the issue by defining a builder class, and that this also works in JavaFX 2.0 and later. The JavaFX team decided (probably correctly) that using this approach in a way that didn't suffer from the bugs that existed in the initial implementation of the builders would add too much bloat to the codebase of the framework.

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

Clearly if you are using JavaFX 8, the constructor annotation approach is much less work.

References:

这篇关于javaFX 8 中@NamedArg 注释的目的是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆