28

私は Swing コンポーネントを実装していますReactor。だから私はこれがうまくいくと思った:

trait Foo[A] extends scala.swing.Publisher {
  final case class Bar(parent: Vector[A], children: A*) extends scala.swing.event.Event
}

trait Test {
  val foo: Foo[Int]

  foo.reactions += {
    case foo.Bar(parent, children) => {
      println(parent.sum - children)
    }
  }
}

残念ながら、2 つのコンパイラ警告が表示されます。

The outer reference in this type test cannot be checked at run time.
  final case class Bar(parent: Vector[A], children: A*) extends scala.swing.event.Event
                   ^
The outer reference in this type test cannot be checked at run time.
    case foo.Bar(parent, children) => {
                ^

これらの警告を無視する必要がありますか? それらを抑制できますか?デザインを変更する必要がありますか?

4

1 に答える 1

41

Scala では、内部クラスは「パス依存」です。

あなたのコードを例として使用します。と という2 つFoo[Int]のがある場合、はfooとは異なるタイプです。これは Java の内部クラスの考え方とは異なることに注意してください。ここで、とは同じ型です。barfoo.Barbar.Barfoo.Barbar.Bar

いずれにせよ、JVM は内部クラスを直接サポートしていないため、Java と Scala の両方で、Bar クラスは という JVM クラスにコンパイルされFoo$Barます。内部クラスのインスタンスには、ほとんどの場合、その所有者への参照 (「外部参照」) が含まれています。

ここで、パス依存型 (コード内のものなど) でパターン マッチが得られると、Scala コンパイラは 2 つのことを行うバイトコードを生成します: クラスをチェックします (つまり、受け取ったオブジェクトがは のインスタンスでありFoo$Bar、外部参照をチェックします (したがって、受け取ったオブジェクトの外部参照が であることをチェックしますfoo)。

ただし、コードでは、内部クラスを final として宣言しているため、コンパイラは外部参照をチェックする方法を見つけることができません。

したがって、警告を無視するとFoo$Bar、たとえ に属していなくても、パターンは のすべてのインスタンスに一致しますfoo。これが問題になるかどうかは、私よりあなたの方がよくわかるでしょう。

または、内部クラスを非最終にすることで修正できます。

追伸、なぜ Scala コンパイラが final 内部クラスの外部参照をチェックできないのか完全にはわかりませんが、内部クラスが final の場合outer$はプライベートであり、非 final の場合outer$はパブリックであることがわかりました。これがなぜなのかを理解するために、コンパイラの内部を調べてみる価値があるかもしれません。

アップデート

これは既知の問題であることが判明しました - SI-4440。Scala コンパイラーは、使用されていない最終内部クラスの外部参照を削除します (サブクラスがそれらを使用する可能性もないため、これは漠然と正当です)。これの肯定的な結果の 1 つは、内部クラスがまだ使用されている間に外部ク​​ラスがガベージ コレクションされる可能性があることです。

于 2013-05-09T16:26:13.937 に答える