首页 文章

Scala名称私有字段和JavaFX FXML注入

提问于
浏览
5

下面的例子和解释很长,所以这里是我的问题的要点:当使用一个坚持执行字段注入的框架(在真正应该保持私有的字段)时,如何处理scalac的名称 - 私有字段的错误?


我正在使用ScalaFX / JavaFX和FXML在Scala中编写应用程序 . 当您使用FXML在JavaFX中定义视图时,FXML中定义的对象(例如按钮和文本字段)将通过以下方式注入控制器:

  • 将一个 fx:id 属性添加到FXML元素

  • 向控制器添加(通常是私有)字段,带有 @FXML 注释,字段名称与FXML中定义的 fx:id 属性值相匹配

  • 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 为空 . 如果我单击复选框,则在调用 testButton.text_= 时,如预期的那样, NullPointerException .

查看编译的类时,原因很明显:

  • 有一个 TestController$$anonfun$initialize$1 类,用于 initialize() 方法中传递给 testCheckBox.selected.onChange() 的匿名函数

  • TestController 类中,有两个私有字段: testCheckBox (如预期的那样)和 test$TestController$$testButton (而不仅仅是 testButton ),以及accessor / mutator方法 . 其中,只有 test$TestController$$testButton 的存取方法是公开的 .

显然,Scala编译器损坏了 testButton 字段的名称,因为它必须公开其访问器方法(从 TestController$$anonfun$initialize$1 访问它),因为字段和accesor / mutator方法应该保持相同的名称 .


现在,最后,我的问题是:有没有合理的解决方案来处理这种情况?现在,我所做的是将字段公开:因为编译器不需要更改它们的可见性,所以它不会破坏它们的名称 . 但是,这些领域确实没有公开的业务 .

注意:另一种解决方案是使用scala-fxml库,它完全隐藏了字段注入,但出于其他原因,我宁愿使用bog标准的FXML加载 .

2 回答

  • 1

    这是我声明注入的字段的方式:

    @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 = _
    
  • 4

    您可以使用@BeanProperty注释字段以阻止Scala重写字段名称 . 唯一的缺点似乎是该字段现在必须至少受到保护,或者Scala编译器抱怨 .

相关问题