I can’t comment this at the language spec/compiler implementation level, but to my understanding, barring specific compiler tricks, there will be one class at the bytecode level per class declaration at the source code level. However, at the source code level the compiler will enforce the perception that for inner class declarations, there will be one class per outer instance.
class Foo {
class Bar // only one Foo$Bar.class
def accept(b: Bar): Unit = ()
def acceptAny(o: AnyRef): Unit =
o match {
case _: Bar => println("Bar")
case _ => println("other")
}
}
val f1 = new Foo
val f2 = new Foo
f1.accept(new f1.Bar) // ok
//f1.accept(new f2.Bar) // doesn't compile
f1.acceptAny(new f1.Bar) // "Bar"
f1.acceptAny(new f2.Bar) // "other"
[EDIT: I guess that the synthetic $outer
reference is used to distinguish cases in the #acceptAny()
dispatch, but I’m not sure.]
With method-local classes in recursive functions, there’s no “anchor” the compiler can use to tie a class reference to a specific recursion level, and pattern matching boils down to reflection in disguise, anyway, so method-local classes basically behave just like inner classes.
def rabbitHole(count: Int): Unit = {
class FooExc extends RuntimeException
try {
if(count <= 0) throw new FooExc else rabbitHole(count - 1)
}
catch {
case e: FooExc =>
println(e.getClass)
throw e
}
}
rabbitHole(2)
Output:
class de.sangamon.scalausers.MemberClassId$FooExc$1
class de.sangamon.scalausers.MemberClassId$FooExc$1
class de.sangamon.scalausers.MemberClassId$FooExc$1
Exception in thread "main" de.sangamon.scalausers.MemberClassId$FooExc$1