2014-11-08 8 views
22

Chciałbym wiedzieć, co jest w przypadku stosowanie @NamedArg adnotacji w JavaFX 8Jaki jest cel adnotacji @NamedArg w javaFX 8?

Javadoc nie daje nam więcej szczegółów, Javadoc: Adnotacja który zawiera informacje na temat nazwy argumentu.

I nie więcej informacji, dokumentacji, przykładów w Internecie.

Może ktoś może pomóc?

Pozdrawiam.

+0

Dziękuję za to pytanie. Nie wiedziałem o '@ NamedArgs', dopóki nie zobaczyłem tego i nie przeszedłem odpowiedzi. Zastanawiałem się, w jaki sposób problem z 'FXMLLoader' polegający na budowaniu zostanie rozwiązany po usunięciu budowniczych, i to też odpowiada na to pytanie. –

Odpowiedz

39

Adnotacja umożliwia FXMLLoader tworzenie instancji klasy, która nie ma konstruktora zerowego argumentu.

Tło techniczne:

FXMLLoader tworzy obiekty przy użyciu odbicia. Zwykle, jeśli używasz znacznika odpowiadającego klasie z konstruktorem nie pobierającym żadnych argumentów, obiekt jest tworzony z tej klasy przez wywołanie Class.newInstance(), które wywołuje konstruktor bezargumentowy.

Jeśli klasa jest zdefiniowana tylko z konstruktorami, które pobierają parametry, jest to problematyczne. Głównym problemem jest to, że specyfikacja języka Java nie wymaga, aby nazwy parametrów (do metod lub konstruktorów) były zachowywane w środowisku wykonawczym. Oznacza to, że nie ma bezpośredniego, gwarantowanego sposobu, aby FXMLLoader określić, który parametr ma daną nazwę.

Aby to beton, załóżmy, że zdefiniowanie klasy Person następująco:

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

} 

W FXML możemy spróbować stworzyć Person następująco:

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

To nie zadziała, ponieważ moduł ładujący FXML nie ma gwarancji, że reprezentacja środowiska wykonawczego klasy Person zachowuje informacje o tym, który parametr konstruktora to firstName i który jest lastName.

historyczny

Java 2.2 zdefiniowanych klas "Builder" odpowiadające każdej kontroli. Te klasy konstruktora stosują standardowy wzorzec budowania. Gdy FXMLLoader napotka znacznik odwołujący się do klasy bez konstruktora zerowego argumentu, użyje odpowiedniego konstruktora do utworzenia instancji.

Niestety, implementacja klas budowniczych była błędna, i they were deprecated in JavaFX 8, i zostaną usunięte w późniejszej wersji (prawdopodobnie JavaFX 9). To pozostawiło problem dla FXMLLoader, który nie będzie już miał klas budowniczych, na których można polegać przy tworzeniu instancji klas bez konstruktora zerowego argumentu. Prawdziwym przykładem jest klasa Color, która nie ma konstruktora zerowego argumentu i zostanie usunięta jego klasa budowniczych.

@NamedArgs

Rozwiązaniem tego było wprowadzać adnotacje, która jest używana do zatrzymywania się nazwą metody (lub konstruktora) argument w czasie pracy.Przez odbicie możemy zapytać listę parametrów konstruktora/metody i uzyskać typ (ale nie nazwę) każdego parametru. Możliwe jest również zapytanie do każdego parametru o dowolne adnotacje i uzyskanie wartości tych adnotacji. Tak więc adnotacja @NamedArg została wprowadzona specjalnie w celu zachowania nazwy parametru w środowisku wykonawczym.

Przykład

Na przykład użyć klasy Person wprowadziliśmy powyżej:

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

Jeśli próbujesz załadować korzystając 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()); 
    } 
} 

potem widzisz błąd w czasie wykonywania:

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

wskazujące na FXMLLoader poszukuje konstruktora biorąc żadnych argumentów (Person.<init>()).

W JavaFX 8, można rozwiązać ten problem poprzez podanie nazwy parametrów z @NamedArg adnotacji:

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

To pozwoli FXMLLoader załadować klasę zgodnie z wymaganiami.

Należy pamiętać, że można również rozwiązać ten problem, definiując klasę konstruktora i działa to również w JavaFX 2.0 i nowszych wersjach. Zespół JavaFX zdecydował (prawdopodobnie poprawnie), że zastosowanie tego podejścia w sposób, który nie ucierpiałby na błędach, które istniały w początkowej fazie implementacji budowniczych, spowodowałoby zbyt duże obciążenie bazy kodu.

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

Oczywiście, jeśli używasz JavaFX 8, podejście do opisu konstruktora jest znacznie mniej pracy.

Odniesienia:

+1

Dziękuję bardzo za kompletną i ilustrowaną odpowiedź, do tej pory musiałem zdefiniować puste konstruktory bez-arg dla moich komponentów JavaFX, to zmieni i oczyści mój kod za pomocą @NamedArg. – izakiel