Скалирование имени в приватных полях и внедрение JavaFX FXML

Следующий пример и пояснения довольно длинные, поэтому суть моего вопроса в том, как бороться с искажением имен в приватных полях при использовании фреймворка, который настаивает на внедрении полей (на полях, которые действительно должны оставаться закрытыми)?


Я пишу приложение в Scala, используя ScalaFX/JavaFX и FXML. Когда вы используете FXML для определения ваших представлений в JavaFX, объекты, определенные в FXML (такие как кнопки и текстовые поля), вводятся в контроллер с помощью:

  • добавив fx:id свойство к элементам FXML
  • добавление (обычно приватных) полей в контроллер, с @FXML аннотации и с именами полей, соответствующими значениям fx:id свойства, определенные в FXML
  • когда FXMLoader создает экземпляр контроллера, он автоматически внедряет fx:id аннотированные элементы в соответствие @FXML аннотированные поля контроллера через рефлексию

Я не большой поклонник инъекций, но так работает FXML. Тем не менее, я столкнулся с неожиданными осложнениями в Scala из-за искажения имен полей, выполняемого компилятором при некоторых обстоятельствах...

Вот пример приложения:

test / TestApp.scala (ничего интересного, просто нужно запустить пример)

package test

import javafx.application.Application
import javafx.fxml.FXMLLoader
import javafx.scene.{Scene, Parent}
import javafx.stage.Stage

object TestApp {
  def main(args: Array[String]) {
    Application.launch(classOf[TestApp], args: _*)
  }
}

class TestApp extends Application {
  override def start(primaryStage: Stage): Unit = {
    val root: Parent = FXMLLoader.load(getClass.getResource("/test.fxml"))
    val scene: Scene = new Scene(root, 200, 200)

    primaryStage.setTitle("Test")
    primaryStage.setScene(scene)
    primaryStage.show()
  }
}

test.fxml (вид)

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
      prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="test.TestController">
    <children>
        <CheckBox fx:id="testCheckBox" mnemonicParsing="false" text="CheckBox"/>
        <Button fx:id="testButton" mnemonicParsing="false" text="Button"/>
    </children>
</VBox>

test / TestController.scala (контроллер для представления test.fxml)

package test

import javafx.fxml.FXML
import javafx.scene.{control => jfxsc}

import scalafx.Includes._

class TestController {
  @FXML private var testCheckBox: jfxsc.CheckBox = _
  @FXML private var testButton: jfxsc.Button = _

  def initialize(): Unit = {
    println(s"testCheckBox=$testCheckBox")
    println(s"testButton=$testButton")

    testCheckBox.selected.onChange {
      testButton.text = "changed"
    }
  }
}

При запуске приложения println заявления показывают, что testCheckBox впрыскивается правильно, но testButton нулевой. Если я нажму на флажок, то, как и ожидалось, NullPointerException при звонке testButton.text_=,

Причина вполне очевидна при взгляде на скомпилированные классы:

  • E сть TestController$$anonfun$initialize$1 класс, для анонимной функции, переданной в testCheckBox.selected.onChange() в initialize() метод
  • в TestController класс, есть два приватных поля: testCheckBox (как и ожидалось) и test$TestController$$testButton (а не просто testButton) и методы доступа / мутатора. Из них только метод доступа для test$TestController$$testButton является публичным.

Ясно, что компилятор Scala исказил имя testButton поле, потому что он должен был сделать свой метод доступа открытым (доступ к нему из TestController$$anonfun$initialize$1) и потому что методы field и accesor/mutator должны иметь одинаковое имя.


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

Примечание. Другим решением будет использование библиотеки scala-fxml, которая полностью скрывает внедрение полей, но я бы предпочел использовать стандартную загрузку FXML по другим причинам.

2 ответа

Вот как я объявляю свои введенные поля:

@FXML
var popoutButton: Button = _

IOW, оставь private и все работает нормально. Чувствуется себя немного грязно, если вы пришли из мира Java, но обходные пути требуют гораздо больше кода.

Контроллеры подразумеваются fx:include также отлично работает:

FXML:

<fx:include fx:id="paramTable" source="ParameterTable.fxml" />

Скала:

@FXML
var paramTable: TableView[(ModelParameter, ParameterValue)] = _
@FXML
var paramTableController: ParameterTableController = _

Вы можете аннотировать свои поля с помощью @BeanProperty, чтобы Scala не переписывала имя поля. Единственным недостатком, по-видимому, является то, что поле теперь должно быть как минимум защищено, или компилятор Scala жалуется.

Другие вопросы по тегам