我知道JavaFX 8中@NamedArg批注的用例是什么

Javadoc没有提供更多详细信息,
Javadoc:提供有关自变量名称的信息的注释。

互联网上没有更多的信息,文档和示例。

也许有人可以帮忙?

问候。

最佳答案

@NamedArg批注允许FXMLLoader实例化没有零参数构造函数的类。

技术背景:

FXMLLoader使用反射创建对象。通常,如果您使用与类相对应的标签,且该类的构造函数不带参数,则通过调用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中,我们可以尝试如下创建Person

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


这是行不通的,因为FXML加载程序无法保证Person类的运行时表示形式会保留有关哪个构造函数参数为firstName和哪个为lastName的信息。

历史背景

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

不幸的是,构建器类的实现存在缺陷,并且是they were deprecated in JavaFX 8,并且将在更高版本中删除(可能是JavaFX 9)。这给FXMLLoader带来了一个问题,它不再具有依赖于生成器的类来实例化没有零参数构造函数的类。一个真实的例子是Color类,它没有零参数构造函数,并且将删除其构造器类。

@NamedArgs

解决此问题的方法是引入注释,该注释用于在运行时保留方法(或构造函数)参数的名称。通过反思,我们可以查询构造函数/方法的参数列表,并获取每个参数的类型(而不是名称)。还可以查询每个参数的任何注释,并获取这些注释的值。因此,专门引入@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根据需要加载类。

请注意,您还可以通过定义构建器类来解决此问题,并且这在JavaFX 2.0和更高版本中也适用。 JavaFX团队决定(可能正确)决定,以这种方式使用不会受到构建器最初实现中存在的错误困扰的方式,会给框架的代码库增加太多膨胀。

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,则构造函数注释方法的工作量要少得多。

参考文献:


Proposal to deprecate builders
Tweak request to add constructor annotations
Builder pattern
FXML documentation(讨论构建器,但不讨论@NamedArg
Request to add documentation on @NamedArgs to "Introduction to FXML" document

09-30 23:41