0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[kotlin] RxJavaFxによるTableViewの計算列へのバンディングでjava.lang.IllegalAccessError出る場合の対処

Posted at

現象

以下のようなPersonデータ

class Person(name: String, birthDay: LocalDate) {
    var nameProperty: StringProperty = SimpleStringProperty(name)
    var birthdayProperty: ObjectProperty<LocalDate> = SimpleObjectProperty(birthDay) 
}

これをTableViewにバインディングする状況を考える。

typealias TableCellFactory<T> = javafx.util.Callback<TableColumn.CellDataFeatures<Person,T>,ObservableValue<T>> 

class AppMain: Application() {
    override fun start(stage: Stage) {
        val table = TableView<Person>()        
        table.setEditable(true)
        table.getItems().setAll(
            Person("A", LocalDate.of(1989,1,18)),
            Person("B",LocalDate.of(1980,5,12)),
            Person("C",LocalDate.of(1975,3,8))        
        )       

        val nameCol = TableColumn<Person,String>("Name")  
        nameCol.cellValueFactory = TableCellFactory<String>{ v -> v.value.nameProperty } 
        
        val birthdayCol = TableColumn<Person,LocalDate>("Birthday")
        birthdayCol.cellValueFactory = TableCellFactory<LocalDate>{ v -> v.value.birthdayProperty }
        birthdayCol.cellFactory = TextFieldTableCell.forTableColumn(LocalDateStringConverter())
        
        table.getColumns().addAll(nameCol, birthdayCol)
        stage.setScene(Scene(table));
        stage.title = "Table View binding"
        stage.width = 300.0
        stage.height = 200.0
        stage.show()
    }
}

これを実行すると、以下のように望んだ結果となる。

スクリーンショット 2021-07-25 22.51.04.png

次に年齢を出すためにPerson型を

class Person(name: String, birthDay: LocalDate) {
    // (snip)
    var ageProperty: Binding<Long> = 
        JavaFxObservable.valuesOf(birthdayProperty)
        .map { d -> ChronoUnit.YEARS.between(d, LocalDate.now()) }
        .to(JavaFxObserver::toBinding)    
}

のように生年月日から年齢を導出するバインディングプロパティを用意する。

またTableViewに年齢列を追加する。

class AppMain: Application() {
    override fun start(stage: Stage) {
        // (snip)        
        val ageCol = TableColumn<Person,Long>("Age")   
        ageCol.cellValueFactory = TableCellFactory<Long>{ v -> v.value.ageProperty }

        table.getColumns().addAll(nameCol, birthdayCol)
        // (snip)
    }
}

これを実行すると、java.lang.IllegalAccessErrorの例外が送出される。

その際のスタックトレースが以下

Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
        at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: java.lang.IllegalAccessError: class io.reactivex.rxjavafx.observers.BindingObserver (in unnamed module @0xeb3ba90) cannot access class com.sun.javafx.binding.ExpressionHelper (in module javafx.base) because module javafx.base does not export com.sun.javafx.binding to unnamed module @0xeb3ba90
        at ...

発生源

おそらく、Person型で追加したBinding型のagepropertyを構成する際の

to(JavaFxObserver::toBinding)

で引っかかっている模様。

対処法

上記のスタックトレースで

module javafx.base does not export com.sun.javafx.binding

に注目する。

RxJavaFxがエクスポートされていないcom.sun.javafx.bindingに依存していることが問題となっている。

したがってcom.sun.javafx.bindingをエクスポートしてやれば解決すると思われる。

gradleであれば、

application {
    mainClass = 'example.AppKt'
}

の箇所を

application {
    applicationDefaultJvmArgs += ["--add-opens", "javafx.base/com.sun.javafx.binding=ALL-UNNAMED"]
    mainClass = 'example.AppKt'
}

として、明示的にエクスポートするようコンパイラに伝える1

この設定を行った上で、実行したところ例外が送出されることなく実行できた。

スクリーンショット 2021-07-25 23.16.14.png

補足事項

Learning RxJava with JavaFXのサンプルコードでは、tornadefxを使用している。
tornadefxにおいても過去にTornadoFX uses internal APIs that are encapsulated in Java 9 #276なissueが上がっている(今は対処済みかもしれないが)

環境

  • kotlin - version 1.4.31
  • JVM - version 16.0.1
  • Gradle - version 7.0.2
  • JavaFX - version 16
  • RxJava - version 2.2.212
  • RxJavaFX - version 2.2.2
  1. gradleしか触ってないので他のビルド環境の指定方法はわかりません。

  2. RxJavaFXがRxJava2にぶら下がっているため2系を使用した。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?