Access Modifier: Private class becomes public

Hi! I wonder why the following is possible…

Private class “VisibilityTest” should not be used outside “thePackage”. But if you access an instance of it via a “Wrapper”-Object, you can access all public members of private class “VisibilityTest”. Why is this possible?

Par contre, if you use the Companion Object to create a new instance of the private type, you have no access to its public members. Why is it not possible this way too?

Never the less - it is impossible to import the private class “VisibilityTest” and use it as Type-Declaration.

Examples:

The “Pakage” with a private Class “VisibilityTest”

package thePackage

// PRIVATE test class with PUBLIC field
private[thePackage] class VisibilityTest(val value:String) // value is a public field

// COMPANION OBJECT
object VisibilityTest {
  def apply: VisibilityTest = new VisibilityTest("Value set by companion object.")
}

// WRAPPER
class Wrapper {
  val visibilityTest = new VisibilityTest("Value set by Wrapper object.")
}

Test-Code lies in another packager (no sub-package of “thePackage”)

val wrapper = new Wrapper()
val wrapped = wrapper.visibilityTest;
//val wrapped:VisibilityTest = wrapper.visibilityTest;
//            ^^^ OK! Not visible. Can not import type.
println(wrapped.value)
//              ^^^ WHY is this possible?

val viaCompanianObject = VisibilityTest
//println(viaCompanianObject.value)
//                           ^^^ OK Not accessible via Companion Object

May be there is a change in one of the last Scala versions? I just read this:
“Scala does not allow you to let a private class escape from its enclosing scope” on this
Site. The compiler allwos the wrapper-type (s. above) to have this method:

def getPrivateTypesObject = visibilityTest

The reason that viaCompanianObject.value didn’t work is that viaCompanianObject in your code is the companion object.

Thank you for the hint. So the thing concerning the companion object is clear now.

If I remove the scope (thePackage) from the private declaration, it is not possible to create a public method or field that let’s a private member (Visibilitytest) break out of it’s scope. Why is this so? Seems strange to me.

The way I understand it private is compiled to private in bytecode most of the time, but (almost?) all other modifiers are compiled to public because the JVM bytecode has no modifiers whose semantics are the same as the ones that Scala has. I think the JVM specification forbids private classes from escaping their scope. Since all other modifiers are only enforced by the Scala compiler itself Scala can be more lenient for those.

I remember from reading some old bugreports that the behavior of access modifiers in Scala is a bit underspecified. This is a nice starting point to read up on it: https://github.com/scala/bug/issues/6794

Thank you Jasper-M. So the java-scopes are directly supported by the jvm. Private without scope becomes 1:1 standard-java-bytecode while private with scope-indication will not become private in java byte-code.

This is what I’v learned about “private” declaration with scope (“thePackage”), which becomes “public” without scope in java byte code:

package thePackage {
  private[thePackage] class VisibilityTest(val value:String)
}

package anotherPackager {


   { // Works!
     val wrapped = wrapper.visibilityTest;
     println(wrapped.value)
   }

   { // Does not work
     val wrapped:otherPackage.VisibilityTest = wrapper.visibilityTest;
     // !!! Can not use Type in value declaration. Type not accessible.
     println(wrapped.value)
   }

   { // Does not work    
     val wrapped:Any = wrapper.visibilityTest;
     println(wrapped.value)
     // !!! Type "Any" has no member "value"
   }
}

In the working example, I think scala is not perfect, because “wrapped” has an infered type (VisibilityTest), but I can not declare this same type explicitly - just type “Any” is possible.