Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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.

like image 893
Robin Dos Anjos Avatar asked Apr 10 '18 20:04

Robin Dos Anjos


People also ask

Why can't a static method refer to non-static members of the class?

Static methods cannot modify Non-static fields since - For using a Non-Static field (outside the class) you must instantiate a class object, But for using a Static method there is no need for object instantiation at all.

Can static inner class have non-static method?

A static nested class does not have a reference to a nesting instance, so a static nested class cannot invoke non-static methods or access non-static fields of an instance of the class within which it is nested.

Can we call non-static method from static?

A static method can call only other static methods; it cannot call a non-static method.

Why we can't use this inside static methods?

No, we can't use “this” keyword inside a static method. “this” refers to current instance of the class. But if we define a method as static , class instance will not have access to it, only CLR executes that block of code. Hence we can't use “this” keyword inside static method.


2 Answers

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     }  } 
like image 81
Andreas Avatar answered Oct 01 '22 14:10

Andreas


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.

like image 27
David Moles Avatar answered Oct 01 '22 15:10

David Moles