I expect this code (calling a method of an anonymous class after using a pattern match on refined type)
(new {
def foo : Unit = println("Called foo method")
} : Any) match {
case f : {def foo : Unit} ⇒
println("Has foo method")
f.foo
}
to print
Has foo method
Called foo method
(as well as an unchecked warning).
I know the match always succeeds due to type erasure, but that shouldn’t cause the problem, since the run-time type (even considering erasure) of f should be $anon$NameOfSomeAnonymousClassThatHasAfooMethod
When entered into the Scala REPL (2.9.1), it actually throws NoSuchMethodException:
<console>:11: warning: refinement AnyRef{def foo: Unit} in type pattern AnyRef{def foo: Unit} is unchecked since it is eliminated by erasure
case f : {def foo : Unit} ⇒
^
Has foo method
java.lang.NoSuchMethodException: $anon$1.foo()
at java.lang.Class.getMethod(Class.java:1622)
at .reflMethod$Method1(<console>:13)
at .<init>(<console>:13)
at .<clinit>(<console>:13)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:679)
Why?
Edit
It turns out the proximate cause is that foo is generated as private. I speculated about the cause of this in my answer, but I’m not sure. If you have an idea, still feel free to post it as an answer!
Upon more investigation, I find that the method was somehow made private:
prints
res5: Array[java.lang.reflect.Method] = Array(private void $anon$1.foo()).This is odd because Scala methods are supposed to be public by default.
As Edmondo1984 points out, it works (
foomethod is public) if you remove the: Any.Speculatively
I suspect the cause is that the compiler wrongly assumes that since the class is anonymous and the instance is declared to be of another type, its defined methods are uncallable from outside the class. This assumption would be valid in Java, but not in a language that provides structural typing. It therefore generates them as private, in an overzealous application of the principle of information hiding. If so, this is either a compiler bug or a language design corner case (using anonymous functions together with structural typing).