私有字段的Scala名称处理和JavaFX FXML注入 [英] Scala name mangling of private fields and JavaFX FXML injection

查看:119
本文介绍了私有字段的Scala名称处理和JavaFX FXML注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The following example and explanations are quite long, so here is the gist of my question: how to deal with scalac's name-mangling of private fields when using a framework which insists on performing field injection (on fields which really should stay private)?

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

I am writing an application in Scala, using ScalaFX/JavaFX and FXML. When you use FXML to define your views in JavaFX, objects defined in FXML (such as buttons and text fields) are injected into the controller by :

  • 向FXML元素添加fx:id属性
  • 使用@FXML批注和与FXML中定义的fx:id属性值匹配的字段名称向控制器添加(通常是私有的)字段
  • FXMLoader实例化控制器时,它会通过反射自动将fx:id注释的元素注入到控制器的匹配@FXML注释的字段中
  • adding an fx:id property to the FXML elements
  • adding (usually private) fields to the controller, with the @FXML annotation and with field names matching the values of the fx:id properties defined in the FXML
  • when the FXMLoader instantiates the controller, it automatically injects the fx:id annotated elements into the matching @FXML annotated fields of the controller through reflexion

我不是字段注入的忠实拥护者,但这就是FXML的工作方式.但是,由于在某些情况下编译器执行了字段名处理,因此我在Scala中遇到了意想不到的麻烦...

I'm not a big fan of field injection, but that's how FXML works. However, I've run into unexpected complications in Scala, due to field name mangling performed by the compiler in some circumstances...

这是一个示例应用程序:

Here is an example application :

test/TestApp.scala(没什么有趣的,只需要运行示例)

test/TestApp.scala (nothing interesting, just needed to run the example)

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(视图)

test.fxml (the view)

<?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视图的控制器)

test/TestController.scala (the controller for the test.fxml view)

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.

When running the application, the println statements show that testCheckBox gets injected properly, but testButton is null. If I click on the checkbox, there is, as expected, a NullPointerException when calling testButton.text_=.

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

The reason is quite obvious when looking at the compiled classes :

  • 有一个TestController$$anonfun$initialize$1类,用于通过initialize()方法传递给testCheckBox.selected.onChange()的匿名函数
  • TestController类中,有两个私有字段:testCheckBox(按预期)和test$TestController$$testButton(而不只是testButton),以及访问器/更改器方法.其中,只有test$TestController$$testButton的访问器方法是公开的.
  • There is a TestController$$anonfun$initialize$1 class, for the anonymous function passed to testCheckBox.selected.onChange() in the initialize() method
  • In the TestController class, there are two private fields : testCheckBox (as expected) and test$TestController$$testButton (rather than just testButton), and the accessor/mutator methods. Of those, only the accessor method for test$TestController$$testButton is public.

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

Clearly, the Scala compiler mangled the name of the testButton field because it had to make its accessor method public (to access it from TestController$$anonfun$initialize$1) and because the field and the accesor/mutator methods should keep the same name.

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

Now, finally, here is my question: is there a reasonable solution to deal with this situation? Right now, what I have done is make the fields public: since the compiler doesn't need to change their visibility, it won't mangle their name. However, those fields really have no business being public.

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

Note: Another solution would be to use the scala-fxml library, which completely hides the field injection, but I'd rather use bog-standard FXML loading for other reasons.

推荐答案

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

This is how I declare my injected fields:

@FXML
var popoutButton: Button = _

IOW,放弃private,一切正常.如果您来自Java世界,感觉会有些脏,但是解决方法需要更多的代码.

IOW, leave off the private and things work fine. Feels a bit dirty if you're coming from the Java world, but workarounds take a lot more code.

fx:include暗示的控制器也可以正常工作:

Controllers implied by fx:include also work fine:

FXML :

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

Scala :

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

这篇关于私有字段的Scala名称处理和JavaFX FXML注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆