I am implementing a Swing component and I want to overcome the #### untypedness of Reactor
. So I thought this would work:
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) } } }
Unfortunately that gives me two compiler warnings:
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) => { ^
Should I ignore these warnings? Can I suppress them? Should I change the design?
In Scala, inner classes are "path-dependent".
I'll use your code as an example. If you have two Foo[Int]
s, called foo
and bar
, then foo.Bar
is a different type to bar.Bar
. Note that this differs from Java's idea of inner classes, where foo.Bar
and bar.Bar
are the same type.
In any case, the JVM doesn't support inner classes directly, so in both Java and Scala, the class Bar compiles to a JVM class called Foo$Bar
. Instances of inner classes almost always contain a reference to their owner - the "outer reference".
Now, when you've got a pattern match on a path dependent type (like the one in your code), the Scala compiler will produce bytecode which does two things: it will check the class (so it will check that the object it received is an instance of Foo$Bar
), and it will check the outer reference (so it will check that the outer reference of the object received is foo
).
However, in your code, the compiler can't find a way to check the outer reference, because you've declared your inner class as final.
So, if you ignore the warning, then your pattern will match all instances of Foo$Bar
, even if they don't belong to foo
. You'll have a better idea than me whether this will be a problem.
Or, you can fix it by making your inner class non-final.
P.s, I'm not entirely sure why the Scala compiler can't check outer references on final inner classes, but I've found that if an inner class is final, then outer$
is private, whereas if it's non-final, outer$
is public. It might be worth a poke around in the compiler's internals, to figure out why this is.
It turns out this is a known issue - SI-4440. The Scala compiler drops outer references for final inner classes, if they're not used (which is vaguely legitimate, as there's no possibility of subclasses using them either). One positive consequence of this is that outer classes can be garbage collected whilst inner classes are still in use, so the Scala devs are reluctant to reintroduce outer references for fear of introducing memory leaks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With