javaFX 8中@NamedArg注解的用途是什么?

问题描述 投票:28回答:1

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

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

在互联网上也没有更多的信息,文档,例子。

也许有人能帮助我们?

Regards.

java javafx annotations
1个回答
50
投票

@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定义了与每个控件相对应的 "构建器 "类。这些构建器类遵循标准的构建器模式。当 FXMLLoader 遇到引用一个没有零参数构造函数的类的标签,它将使用相应的构造器来创建实例。

不幸的是,构建器类的实现存在缺陷,而且 它们在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团队决定(可能是正确的),使用这种方式不会受到构建器初始实现中存在的bug的影响,会给框架的代码库增加太多的臃肿。

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,构造函数注释的方法要省事得多。

参考文献。

© www.soinside.com 2019 - 2024. All rights reserved.