【问题标题】:Scala name mangling of private fields and JavaFX FXML injection私有字段的 Scala 名称修改和 JavaFX FXML 注入
【发布时间】:2015-10-23 14:21:18
【问题描述】:

以下示例和解释很长,所以这是我的问题的要点:当使用坚持执行字段注入的框架时,如何处理 scalac 对私有字段的名称修饰(在真正应该保持私有的字段上) )?


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

  • 向 FXML 元素添加 fx:id 属性
  • 向控制器添加(通常是私有的)字段,带有 @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),以及访问器/修改器方法。其中,只有test$TestController$$testButton 的访问器方法是公开的。

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


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

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

【问题讨论】:

  • 除了编写 scalafxml 库之外,我找不到任何其他解决方案:/ 顺便说一句,我很好奇你为什么不喜欢使用它。
  • 我实际上非常喜欢 scalafxml 的想法,但是当我尝试它时,我在尝试从 &lt;fx:includes&gt; 获取自定义控件或特别是控制器注入工作时遇到了问题......而且因为我还在对 JavaFX/ScalaFX(以及 Scala 语言本身)来说相当新,我不想花时间在这类事情上,特别是因为我不知道我是否会遇到其他与 scalafxml 冲突的功能,而且我没有有足够的信心尝试为他们贡献我自己的解决方案。不过,我很确定我会在未来的某个时候再次尝试 scalafxml。

标签: scala javafx-8 fxml scalafx


【解决方案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 = _

【讨论】:

  • @FXML var popoutButton: Button = _ 在找到这个答案之前我尝试了同样的事情并且没有任何运气。这对任何人有用吗?
  • 它仍然适用于我们。有什么错误,只是为了确认一下,您的字段是 class 而不是 object
  • 我在尝试从具有类似声明的控制器类访问 Stage 时遇到 NPE。我想我可以通过设置和获取两个类之外的object 来解决问题。感谢您回来。
  • 我的经验是,如果您在初始化阶段依赖Stage,还有另一种思考方式。我通常会在我必须拥有它以按需构建另一个对象时抓住Stage。这可能意味着需要一个单独的 FXML 文件来保持模块化。
【解决方案2】:

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

【讨论】:

  • 我认为这并不完全正确。我发现是 private 可见性说明符打开了名称重写。
猜你喜欢
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 2010-11-12
  • 1970-01-01
  • 2016-03-25
  • 1970-01-01
  • 1970-01-01
  • 2020-07-17
相关资源
最近更新 更多