¿Cuál es el propósito de la anotación @NamedArg en javaFX 8?

Sabría cuál es el caso de uso de la anotación @NamedArg en JavaFX 8

El javadoc no nos da más detalles, Javadoc: anotación que proporciona información sobre el nombre del argumento.

Y no más información, documentación, ejemplos en internet.

Tal vez alguien podría ayudar?

Saludos.

La anotación @NamedArg permite a un FXMLLoader crear instancias de una clase que no tiene un constructor de argumento cero.

Experiencia técnica:

FXMLLoader crea objetos usando la reflexión. Normalmente, si utiliza una etiqueta correspondiente a una clase con un constructor sin argumentos, se crea un objeto de esa clase llamando a Class.newInstance() , que invoca el constructor sin argumentos.

Si una clase se define solo con constructores que toman parámetros, entonces esto es problemático. El problema principal es que la especificación del lenguaje Java no requiere que los nombres de los parámetros (a los métodos o constructores) se conserven en el tiempo de ejecución. Esto significa que no hay una manera directa y garantizada para que FXMLLoader determine qué parámetro tiene un nombre de stack.

Para hacer esto concreto, supongamos que definimos una clase Person la siguiente manera:

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

En FXML podríamos intentar crear una Person siguiente manera:

  

Esto no funcionará, porque el cargador FXML no tiene garantía de que la representación en tiempo de ejecución de la clase Person retenga la información en cuanto a qué parámetro de constructor es firstName y cuál es lastName .

Antecedentes históricos

Java 2.2 define las clases de “Generador” correspondientes a cada control. Estas clases de constructores siguen el patrón de generador estándar. Cuando FXMLLoader encuentra una etiqueta que hace referencia a una clase sin un constructor de argumentos cero, usaría el constructor correspondiente para crear la instancia.

Desafortunadamente, la implementación de las clases de comstackdor fue defectuosa, y se desaprobaron en JavaFX 8 , y se eliminarán en una versión posterior (probablemente JavaFX 9). Esto dejó un problema para FXMLLoader , que ya no tendría clases de FXMLLoader que confiar para crear instancias de clases sin un constructor de argumentos cero. Un ejemplo real es la clase Color , que no tiene un constructor de argumento cero y tendrá su clase de constructor eliminada.

@NamedArgs

La solución para esto fue introducir una anotación que se utiliza para retener el nombre de un argumento de método (o constructor) en tiempo de ejecución. Por reflexión, podemos consultar la lista de parámetros de un constructor / método y obtener el tipo (pero no el nombre) de cada parámetro. También es posible consultar cada parámetro para cualquier anotación y obtener el valor de esas anotaciones. Por lo tanto, la anotación @NamedArg se introdujo específicamente con el fin de conservar un nombre de un parámetro en tiempo de ejecución.

Ejemplo

Para un ejemplo, use la clase Person que presentamos anteriormente:

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

Si intenta cargar esto usando FXML:

Person.fxml:

    

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

entonces ves un error en el tiempo de ejecución:

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

indicando que FXMLLoader está buscando un constructor sin argumentos ( Person.() ).

En JavaFX 8, puede solucionar el problema especificando el nombre de los parámetros con la anotación @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); } } 

Esto permitirá que FXMLLoader cargue la clase según sea necesario.

Tenga en cuenta que también puede solucionar el problema definiendo una clase de generador, y que esto también funciona en JavaFX 2.0 y posterior. El equipo de JavaFX decidió (probablemente correctamente) que el uso de este enfoque de una manera que no sufriera los errores que existían en la implementación inicial de los constructores agregaría demasiada inflamación a la base de código del marco.

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

Claramente, si está utilizando JavaFX 8, el enfoque de anotación del constructor es mucho menos trabajo.

Referencias

  • Propuesta para depreciar a los constructores
  • Tweak request para agregar anotaciones de constructor
  • Patrón de constructor
  • Documentación de FXML (discute los constructores, pero no @NamedArg )
  • Solicitud para agregar documentación en @NamedArgs al documento “Introducción a FXML”