Robin Dos Anjos
Robin Dos Anjos

Reputation: 359

Why can’t this static inner class call a non-static method on its outer class?

I'm currently reading Effective Java by Joshua Bloch and I love it! But on page 112 (Item 24) Bloch writes:

A static member class is the simplest kind of nested class. It is best thought of as an ordinary class that happens to be declared inside another class and has access to all of the enclosing class’s members, even those declared private.

And that really confuses me. I would rather say:

A static member class is the simplest kind of nested class. It is best thought of as an ordinary class that happens to be declared inside another class and has access to all of the enclosing class’s static members, even those declared private.

Here is a snippet that illustrates my understanding of the quote:

public class OuterClass {

    public void printMessage(String message) {
        System.out.println(message);
    }

    private static class InnerClass {

        public void sayHello() {
            printMessage("Hello world!"); //error: Cannot make a static reference to the non-static method printMessage(String)
        }

    }
}

You can see that InnerClass's sayHello method does not have access to OuterClass's printMessage method as it is declared in a static inner class while the printMessage method is an instance method. It looks like the author suggests that a static member class can access nonstatic fields of the enclosing class. I am convinced that I have misunderstood something in his last sentence but I cannot figure out what. Any help will be appreciated!

edit: I changed the visibility of the two methods because it is irrelevant to my question. I'm interested in static members, not private members.

Upvotes: 25

Views: 5063

Answers (5)

Vinicius
Vinicius

Reputation: 1701

The way I see it, the text is absolutely right. Static member classes can access the private members of the enclosing classes (sort of). Let me show you an example:

public class OuterClass {
    String _name;
    int _age;
    public OuterClass(String name) {
        _name = name;
    }
    public static OuterClass CreateOuterClass(String name, int age) {
        OuterClass instance = new OuterClass(name);
        instance._age = age; // Notice that the private field "_age" of the enclosing class is visible/accessible inside this static method (as it would also be inside of a static member class).
        return instance;
    }
}

Upvotes: 0

David Moles
David Moles

Reputation: 51093

Note the error message. It's not saying you don't have access. It's saying the method cannot be called. Instance methods don't mean anything without an instance to call them on. What the error message is telling you is that you don't have that instance.

What Bloch is telling you is that if that instance existed, code in the inner class could call private instance methods on it.

Say we have the following class:

public class OuterClass {
  public void publicInstanceMethod() {}
  public static void publicClassMethod() {}
  private void privateInstanceMethod() {}
  private static void privateClassMethod() {}
}

If we try to call those private methods from some random class, we can't:

class SomeOtherClass {
  void doTheThing() {
    OuterClass.publicClassMethod();
    OuterClass.privateClassMethod(); // Error: privateClassMethod() has private access in OuterClass
  }
  void doTheThingWithTheThing(OuterClass oc) {
    oc.publicInstanceMethod();
    oc.privateInstanceMethod();      // Error: privateInstanceMethod() has private access in OuterClass
  }
}

Note that those error messages say private access.

If we add a method to OuterClass itself, we can call those methods:

public class OuterClass {
  // ...declarations etc.
  private void doAThing() {
    publicInstanceMethod();  // OK; same as this.publicInstanceMethod();
    privateInstanceMethod(); // OK; same as this.privateInstanceMethod();
    publicClassMethod();
    privateClassMethod();
  }
}

Or if we add a static inner class:

public class OuterClass {
  // ...declarations etc.
  private static class StaticInnerClass {
    private void doTheThingWithTheThing(OuterClass oc) {
      publicClassMethod();  // OK
      privateClassMethod(); // OK, because we're "inside"
      oc.publicInstanceMethod();  // OK, because we have an instance
      oc.privateInstanceMethod(); // OK, because we have an instance
      publicInstanceMethod();  // no instance -> Error: non-static method publicInstanceMethod() cannot be referenced from a static context
      privateInstanceMethod(); // no instance -> Error: java: non-static method privateInstanceMethod() cannot be referenced from a static context
    }
  }
}

If we add a non-static inner class, it looks like we can do magic:

public class OuterClass {
  // ...declarations etc.
  private class NonStaticInnerClass {
    private void doTheThing() {
      publicClassMethod();     // OK
      privateClassMethod();    // OK
      publicInstanceMethod();  // OK
      privateInstanceMethod(); // OK
    }
  }
}

However, there's trickery going on here: a non-static inner class is always associated with an instance of the outer class, and what you're really looking at is:

  private class NonStaticInnerClass {
    private void doTheThing() {
      publicClassMethod();     // OK
      privateClassMethod();    // OK
      OuterClass.this.publicInstanceMethod();  // still OK
      OuterClass.this.privateInstanceMethod(); // still OK
    }
  }

Here, OuterClass.this is special syntax for accessing that outer instance. But you only need it if it's ambiguous, e.g. if the outer and inner classes have methods with the same name.

Note too that the non-static class can still do the things the static one can do:

  private class NonStaticInnerClass {
    private void doTheThingWithTheThing(OuterClass oc) {
      // 'oc' does *not* have to be the same instance as 'OuterClass.this'
      oc.publicInstanceMethod();
      oc.privateInstanceMethod();
    }
  }

In short: public and private are always about access. The point Bloch is making is that inner classes have access that other classes don't. But no amount of access allows you to call an instance method without telling the compiler what instance you want to call it on.

Upvotes: 9

Andreas
Andreas

Reputation: 159086

Just because InnerClass is static, doesn't mean it couldn't obtain a reference to an instance of OuterClass through other means, most commonly as a parameter, e.g.

public class OuterClass {

    private void printMessage(String message) {
        System.out.println(message);
    }

    private static class InnerClass {

        private void sayHello(OuterClass outer) {
            outer.printMessage("Hello world!"); // allowed
        }

    }
}

If InnerClass had not been nested inside OuterClass, it would not have had access to the private method.

public class OuterClass {

    private void printMessage(String message) {
        System.out.println(message);
    }

}

class InnerClass {

    private void sayHello(OuterClass outer) {
        outer.printMessage("Hello world!"); // ERROR: The method printMessage(String) from the type OuterClass is not visible
    }

}

Upvotes: 49

Kris
Kris

Reputation: 3361

But, that the static inner class doesn't have access to the printMessage function doesn't have to do with that it is an inner class, but that it is static and can't invoke a non-static method. I think that the use of the word "static" you proposed was implicit in the first sentence. What he is pointing out, or chose to emphasize, is just that the inner class can still access private methods of its parent class. He might have just though it unnecessary or confusing to make the static/non-static distinction in the same sentence, too.

Upvotes: 4

Ivan
Ivan

Reputation: 8758

The way you showed it requires inheritance. But methods and fields could be access in this way:

public class OuterClass {

  private void printMessage(String message) {
    System.out.println(message);
  }

  private static class InnerClass {

    private void sayHello() {
        OuterClass outer = new OuterClass();
        outer.printMessage("Hello world!"); 
    }

  }
}

Upvotes: 6

Related Questions