Scala: Accessing package visible methods through structural types outside the package
Asked Answered
K

1

14

This does not work as expected (since I am trying to call a package private run from outside Services):

object Services {
 class HelloPrinter {
   private[Services] def run = "Hello"
  }  
}

val obj = new Services.HelloPrinter

But, surprisingly this works:

val obj: {def run: String} = new Services.HelloPrinter
obj.run

I would say, its a bug in the compiler since as HelloPrinter does not match the structural type because of package visibility rules, it should not compile at all!

Here is a case where the program compiles but it throws a runtime exception (java.lang.NoSuchMethodException):

class HelloPrinter {
  private[HelloPrinter] def run = "Hello"
}  

val obj: {def run: String} = new HelloPrinter
obj.run

Is this a language feature or rule I am missing or legitimately a bug in Scala?

Killigrew answered 8/7, 2015 at 20:48 Comment(3)
Very interesting. Did you try to run with scala -feature REPL? It clearly shows that there is a reflective call invoked. I guess scoping and immutability are all out of the window when it comes to reflection.Selfdefense
Well as far as I can tell the warning is more intended to make you cautious of the possible performance consequences. My understanding is that despite the use of reflection to perform the actual call at runtime, structural typing is (supposedly) as statically typed as anything else in scala, because the compiler will check that the signatures strictly match (and thus the reflective call can only succeed). Unless of course you perform an explicit downcast to a structural type. However here there is no cast, the compiler simply seems to omit checking the visibility of the method.Thremmatology
The second example that you say throws a NoSuchMethodException doesn't compile for me (in the 2.10.4 and 2.11.6 REPL).Forbidden
H
4

On the JVM level visibility scoped to surrounding instances/types does not exist. The Scala compiler will generate a public method in this case and handle this visibility internally.

If you use structural types the compiler will reflectively access the members of this type. It will not check Scala-specific visibility flags but only the ones defined in the Java bytecode.

You did not mention which version of the Scala compiler you are using but I assume that this is a bug in your specific version. I get the same result as Jasper-M when trying to compile it. The reason is that the method that is generated by the compiler is actually prefixed with the type name, i.e. HelloPrinter$$run in this case. The following code will execute:

val x: { def HelloPrinter$$run: String  } = new HelloPrinter
x.run

Again the Scala compiler just generates a public method and manages visibility internally. It is not a feature but rather a bug that the compiler does not check the Scala-internal visibility for structural types.

Housewifely answered 17/7, 2015 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.