Typesafe Swing事件 - "在运行时无法检查此类型测试中的外部引用"

0__*_*0__ 22 swing scala pattern-matching type-erasure

我正在实现一个Swing组件,我想要克服#### untypedness 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)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这给了我两个编译器警告:

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) => {
                ^
Run Code Online (Sandbox Code Playgroud)

我应该忽略这些警告吗?我可以抑制它们吗?我应该改变设计吗?

Jam*_*pic 34

在Scala中,内部类是"路径依赖的".

我将以您的代码为例.如果你有两个Foo[Int]s,叫做foobar,那么foo.Bar就是一个不同的类型bar.Bar.请注意,这与Java的内部类的概念不同,其中foo.Barbar.Bar类型相同.

在任何情况下,JVM都不直接支持内部类,因此在Java和Scala中,类Bar编译为一个名为的JVM类Foo$Bar.内部类的实例几乎总是包含对其所有者的引用 - "外部引用".

现在,当你在路径依赖类型(如代码中的那个)上获得模式匹配时,Scala编译器将生成字节码,它执行两件事:它将检查类(因此它将检查它收到的对象)是一个实例Foo$Bar),它将检查外部引用(因此它将检查所接收对象的外部引用是否是foo).

但是,在您的代码中,编译器无法找到检查外部引用的方法,因为您已将内部类声明为final.

因此,如果您忽略该警告,那么您的模式将匹配所有实例Foo$Bar,即使它们不属于foo.你会比我更好,这是否会成为一个问题.

或者,您可以通过使您的内部类非最终来修复它.

Ps,我不完全确定为什么Scala编译器无法检查最终内部类的外部引用,但我发现如果内部类是final,那么outer$是私有的,而如果它是非final的,outer$则是public.在编译器的内部可能值得一试,找出原因.

更新

事实证明这是一个已知的问题 - SI-4440.如果不使用Scala编译器,则删除最终内部类的外部引用(这是非常合法的,因为子类也不可能使用它们).这样做的一个积极结果是外部类可以被垃圾收集,而内部类仍在使用中,因此Scala开发人员不愿意重新引入外部引用,因为害怕引入内存泄漏.

  • 好的,所以`final`关键字就是问题所在.我不会得出那个结论.在我的情况下,我只想要一种类型安全的方式来保留`A`,所以`foo.Bar`和`bar.Bar`之间的别名实际上并不重要,但编译器警告很烦人.所以我会放弃`final`.(我对'最终案例类'有些神经质,IMO Scala默认情况下应该使案例类准最终,没有理由不这样做). (3认同)