FX 11: загрузка контроллера в Baseclass - PullRequest
0 голосов
/ 18 января 2019

Внедрение FXML в производный класс FX (контроллер) из базового класса работает, но почему?

Код, приведенный ниже, действительно работает.Но мне любопытно, почему?

FXML загружается в конструктор абстрактного базового класса (FXMLPopup) и внедряется в производный класс (TestfxmlController).

Моя проблема: когда базовый класссоздается (и вводится fxml), производный класс еще не создан.Также imho, база не должна ничего знать о производном классе, не так ли?

Кроме того, поле, которое должно быть введено, является частным в производном классе!Таким образом, загрузчик должен сделать его доступным, но в базе нет @FXML, который бы позволил сделать это (разрешение дано только в производном классе, который еще не был создан - Ну, поле вообще не существуетв базе!).

Тем не менее, FXML правильно вводится в производный класс - и поля фактически являются теми, которые находятся в производном классе.Почему это работает?

Базовый класс:

public abstract class FXMLPopup extends Popup implements Initializable {

    @SuppressWarnings("LeakingThisInConstructor")
    public FXMLPopup(String filename) {
        super();
        final FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource(filename));
        //if a controller is set in the fxml, ignor it.
        loader.setControllerFactory(p -> this);
        try {
            this.getContent().add(loader.load());
        } catch (IOException ex) { }
     }
}

Производный класс:

public class TestfxmlController extends FXMLPopup {

    @FXML
    private ChoiceBox<String> testChoiceBox;

    public TestfxmlController() {
        super("fxml/testfxml.fxml");
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        //works!!!!
        testChoiceBox.getItems().add("test");
    }
}

FXMLCode:

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="TestfxmlController">
   <children>
      <ChoiceBox fx:id="testChoiceBox" layoutX="113.0" layoutY="160.0" prefWidth="150.0" />
   </children>
</AnchorPane>

Что бы я ожидал?Я ожидал бы ошибок над ошибками.То, что загрузчик не находит поля в базовом классе, и что доступ запрещен .... Но это как-то magicaly работает безупречно.Несмотря на то, что я нарушаю все возможное в таком маленьком примере.Я хотел бы понять "магию" за этим ...

1 Ответ

0 голосов
/ 18 января 2019

Я думаю, что понимаю это сейчас. Производный класс уже создается при вызове конструктора базового класса - только не инициализирован.

Таким образом, загрузчик фактически получает производный класс. При этом данное отражение может возвращать поля из производного класса.

При этом для базового класса становится возможным инициализировать поля производного класса - даже если базовый класс не имеет никакой информации о его будущих производных. Это не нужно. Он получает эту информацию через отражение (rsp. Загрузчик делает).

Это даже не грубо, поскольку производный класс фактически известен через отражение и, следовательно, известно, что он имеет правильный тип.

Так что теперь я думаю, что этот общий код всплывающего окна FXML на самом деле совершенно допустим.

Несмотря на то, что данный конкретный случай является правильным, похоже, что в загрузчике FXML есть недостаток, когда дело доходит до документированного варианта использования.

Причина: если кто-то создает элемент управления, загруженный из fxml-файла способом, задокументированным (не в этом сценарии использования), и распространяет его как библиотеку, пользователь этой библиотеки может создать его подкласс. Загрузчик теперь будет вставлять в подкласс, а не в поля, которые он намеревался создать, вызывая сбой элемента управления (в этом случае поля в классе библиотеки не инициализируются).

Итак, еще раз: хотя рассматриваемый код работает надежно, при таком поведении документированный вариант использования может привести к проблемам.

...