5

I was shocked today to discover that this code compiles cleanly in Java:

public class A {
  public static class B {
     private static void x() {}
  }
  private static class C {
     private /* So, private to what exactly? */ static void x() {}
  }
  public static void main(String[] args) {
    B.x(); C.x();
  }
}

It seems Java's private keyword indicates only top-level-class-privacy; which, given the general convention to name Java source files by class, means that Java effectively provides only file-level private visibility, not true private visibility. (The equivalent code in C++ does not compile with a visibility error.)

Would anyone be able to explain why Java was designed this way? Or is this just Java's way of telling everyone not to use nested classes? (It certainly hacks in an implementation of a two-way C++ friend relationship, but we already have package-level visibility for that; this seems to make the one-way equivalent impossible. Moreover, it necessitates the compiler creating more hidden accessor methods to circumvent the visibility, and under-the-hood downgrading of nested class visibility control to make all this not error out at runtime. Really, why the trouble?)

2
  • 2
    Why can outer Java classes access inner class private members from StackOverflow
    – user40980
    Jul 4, 2013 at 2:24
  • The benefits and costs are various and not easily weighed against each other, but you are right that it is somewhat surprising. For reference, the original author of javac, Martin Odersky, seems to feel the same way: when inventing Scala, he made the effect configurable, so that the programmer can decide whether something is private to its class, to its class and superclass, or to the file, etc. Jul 4, 2013 at 6:48

1 Answer 1

3

Welcome to Java.

Your code is in the file for class A. The word "private" in this file means private to class A. Class B is just a part of class A. However, the "private" for method x does serve an important purpose; it means that no code outside of class A can call that method on an instance of class B. This means that while an instance of class B could be available all across the program, any problems caused by method x must be caused somewhere in class A. In a large, complex system this encapsulation makes life a lot easier, both for debugging and for modifying code.

The idea here is that an object of class A can communicate with the rest of the system with instances of classes like B and C while keeping a great deal of control over those instances--since method x can only be called from within A. (And if the classes were not static, A would have even more ways to control B and C.)

In Java and C#, nested classes are completely different things with completely different purposes; do not confuse them. In C#, they're just normal classes with slightly limited scope. In Java, nested classes are very much the agents, or extensions, or representives, of their top-level ancestor. The Java approach won't make too much sense until you work with it a while on a complex system, but if you do, it will become very addictive.

1
  • In C#, a class nested within within a generic class definition will actually behave as a family of class definitions (one for each combination combination of type parameters used in the parent class). For example, given public class Foo<T> { public class Bar { public static int moo; }}, the values of Foo<int>.Bar.moo and Foo<String>.Bar.moo will be independent. That's a behavior which is hardly the same as "limited scope".
    – supercat
    Mar 22, 2014 at 19:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.