class ExtensionBase {
final String field;
ExtensionBase(String field) {
this.field = field;
}
ExtensionBase() {
field = "none";
}
public String toString() { return field; }
}
class PackageScope(name :String) extends ExtensionBase(name)
package not_root {
class PublicScope extends PackageScope("Now ExtensionBase is made public")
object Test extends App {
println(new PublicScope)
}
}
Frankly I noticed that quite a while ago, but was sure itās a bug that would cause the JVM to throw an exception, but it seems it works just fine.
For what itās worth, it is at least consistent with method overrides, which can also make package-scoped methods visible to the outside by subclasses.
Iām not sure which access you object to, but I did lodge this question with Scala 3, when I was learning about ātop-level definitionsā.
The terminology is not āroot packageā but āempty packageā (where the name is empty). Things in the empty package are visible to each other, but you canāt import from the empty package. So the additional rule is that things in the empty package are visible in the file where they are defined.
I see there is a code comment in the fix for that issue:
* - Members of the empty package can be accessed only from within the empty package.
* Note: it would be cleaner to never nest package definitions in empty package definitions,
* but then we'd have to give up the fiction that a compilation unit consists of
* a single tree (because a source file may have both toplevel classes which go
* into the empty package and package definitions, which would have to stay outside).
* Since the principle of a single tree per compilation unit is assumed by many
* tools, we did not want to take that step.
There are old Scala 2 tickets about things seeming to leak from package privacy, and there are a few puzzlers or limitations in that area. Iām not sure you intend those cases.
Frankly I havenāt tried it in Java, but you right. To me however, a situation in which anything declared as package protected becomes visible outside the package in any way seems like an issue. If I hide something, I donāt want anyone else creating dependencies on it. Here the class (or rather, its API) is made visible for everyone.
While now I can see perhaps some use in this in implementation inheritance, where the user does not depend (at least in the sources) on the protected class, only its methods exposed through references to the public class, I donāt know if itās the better choice. If itās per spec, then I guess my question is answered.
Just to clarify, _root_ is a kind of pragma for ārootingā an import to ensure that itās not a package-relative import.
The āempty packageā is different. One way the distinction is important is with nested package clauses, which make members of āoutsideā packages visible. One canāt use package _root_ to access members of the empty package. I just happened to get an error that uses the internal name of the empty package:
call site: object Foo in package <empty>
I went looking for those similar tickets and went down the wrong rabbit hole.
Iād argue ExtensionBase and its package-private parts arenāt visible outside itās package. You canāt access it by its name, nor can you access any of its package-private members on PublicScope (e.g. new PublicScope.field will not compile, val x: ExtensionBase = new PublicScope also wonāt).
The client code doesnāt know about the base class per se, it only knows the public API of PackageScope. So you always may remove ExtensionBase, as long as PackageScope implements its public API. I donāt think this kind of indirect dependency is a problem.
I agree this trick can be used to bypass information hiding (when it works) and monkey-patch Java libraries ā Iāve done it. OTOH, using reflection is probably more powerful, just hard to use.
Youāre technically right in a sense, but packages remain an ineffective way to enforce information hiding. If you stick to whatās guaranteed by the compiler, package-private members are effectively part of the public API of a library, and changing them in any way can break source/binary compatibility with existing users, or simply make existing clients misbehave at runtime, even when fully preserving the API of public and protected members. I assume most library authors will refuse to give any support for code playing such tricks, but this is just an unenforced convention.
Iāve heard of a couple of solutions but I have not investigated closely. JVM modules (from Java 9) seemed like they might give stronger guarantees; IIUC, modules align with ācollection of code from an author/organizationā, so if the authors of module1 make something private to a module, the authors of module2 cannot access it.
Before Java 9, I think OSGi also had some of the benefits: in particular IIUC, OSGi will refuse load modules that extend packages from other components. OTOH, OSGiās learning curve seemed extremely steep.