Подтвердить что ты не робот

В чем цель аннотации @NamedArg в javaFX 8?

Я бы знал, что является прецедентом аннотации @NamedArg в JavaFX 8

Javadoc не дает нам более подробной информации, Javadoc: аннотация, содержащая информацию о имени аргумента.

И больше информации, документации, примеров в Интернете нет.

Может кто-нибудь может помочь?

С уважением.

4b9b3361

Ответ 1

Аннотация @NamedArg позволяет FXMLLoader создавать экземпляр класса, у которого нет конструктора с нулевым аргументом.

Техническая информация:

FXMLLoader создает объекты с использованием отражения. Как правило, если вы используете тег, соответствующий классу с конструктором без аргументов, объект создается из этого класса, вызывая Class.newInstance(), который вызывает конструктор без аргументов.

Если класс определен только с конструкторами, которые принимают параметры, то это проблематично. Основная проблема заключается в том, что Java Language Specification не требует, чтобы имена параметров (для методов или конструкторов) сохранялись во время выполнения. Это означает, что нет прямого и гарантированного способа для 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 встречается с тегом, ссылающимся на класс без конструктора с нулевым аргументом, для создания экземпляра он должен использовать соответствующий конструктор.

К сожалению, реализация классов-конструкторов была ошибочной, а они устарели в 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, подход аннотации конструктора гораздо меньше работает.

Литература: