Why can't we have static method in a (non-static) inner class (pre-Java 16)?

Viewed 74691

Why can't we have static method in a non-static inner class?

public class Foo {
    class Bar {
        static void method() {} // Compiler error
    }
}

If I make the inner class static it works. Why?

public class Foo {
    static class Bar { // now static
        static void method() {}
    }
}

In Java 16+, both of these are valid.

15 Answers

Because an instance of an inner class is implicitly associated with an instance of its outer class, it cannot define any static methods itself. Since a static nested class cannot refer directly to instance variables or methods defined in its enclosing class, it can use them only through an object reference, it's safe to declare static methods in a static nested class.

There's not much point to allowing a static method in a non-static inner class; how would you access it? You cannot access (at least initially) a non-static inner class instance without going through an outer class instance. There is no purely static way to create a non-static inner class.

For an outer class Outer, you can access a static method test() like this:

Outer.test();

For a static inner class Inner, you can access its static method innerTest() like this:

Outer.Inner.innerTest();

However, if Inner is not static, there is now no purely static way to reference the method innertest. Non-static inner classes are tied to a specific instance of their outer class. A function is different from a constant, in that a reference to Outer.Inner.CONSTANT is guaranteed to be unambiguous in a way that a function call Outer.Inner.staticFunction(); is not. Let's say you have Inner.staticFunction() that calls getState(), which is defined in Outer. If you try to invoke that static function, you now have an ambiguous reference to the Inner class. That is, on which instance of the inner class do you invoke the static function? It matters. See, there is no truly static way to reference that static method, due to the implicit reference to the outer object.

Paul Bellora is correct that the language designers could have allowed this. They would then have to carefully disallow any access to the implicit reference to the outer class in static methods of the non-static inner class. At this point, what is the value to this being an inner class if you cannot reference the outer class, except statically? And if static access is fine, then why not declare the whole inner class static? If you simply make the inner class itself static, then you have no implicit reference to the outer class, and you no longer have this ambiguity.

If you actually need static methods on a non-static inner class, then you probably need to rethink your design.

I have a theory, which may or may not be correct.

First, you should know some things about how inner classes are implemented in Java. Suppose you've got this class:

class Outer {
    private int foo = 0;
    class Inner implements Runnable {
        public void run(){ foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(); }
}

When you compile it, the generated bytecode will look as if you wrote something like this:

class Outer {
    private int foo = 0;
    static class Inner implements Runnable {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public void run(){ this$0.foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(this); }
}

Now, if we did allow static methods in non-static inner classes, you might want to do something like this.

class Outer {
    private int foo = 0;
    class Inner {
        public static void incrFoo(){ foo++; }
    }
}

... which looks fairly reasonable, as the Inner class seems to have one incarnation per Outer instance. But as we saw above, the non-static inner classes really are just syntactic sugar for static "inner" classes, so the last example would be approximately equivalent to:

class Outer {
    private int foo = 0;
    static class Inner {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public static void incrFoo(){ this$0.foo++; }
    }
}

... which clearly won't work, since this$0 is non-static. This sort of explains why static methods aren't allowed (although you could make the argument that you could allow static methods as long as they didn't reference the enclosing object), and why you can't have non-final static fields (it would be counter-intuitive if instances of non-static inner classes from different objects shared "static state"). It also explains why final fields are allowed (as long as they don't reference the enclosing object).

From: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's methods and fields. Also, because an inner class is associated with an instance, it cannot define any static members itself.

Oracle's explanation is superficial and handwavy. Since there's no technical or syntactic reason to preempt static members within an inner class (it's allowed in other languages such as C#) the Java designers' motivation was likely conceptual taste and/or a matter of technical convenience.

Here's my speculation:

Unlike top-level classes, inner classes are instance-dependent: an inner-class instance is associated with an instance of every one of its outer classes and has direct access to their members. This is the chief motivation for having them in Java. Expressed another way: an inner class is meant for instantiation in the context of an outer class instance. Without an outer class instance, an inner class ought not be any more usable than the other instance members of the outer class. Let's refer to this as the instance-dependent spirit of inner classes.

The very nature of static members (which are NOT object-oriented) clashes with the instance-dependent spirit of inner classes (which IS object-oriented) because you can reference/call a static member of an inner class without an outer class instance by using the qualified inner class name.

Static variables in particular may offend in yet another way: two instances of an inner class that are associated with different instances of the outer class would share static variables. Since variables are a component of state, the two inner class instances would, in effect, share state independently of the outer class instances they're associated with. It’s not that it’s unacceptable that static variables work this way (we accept them in Java as a sensible compromise to OOP purity), but there’s arguably a deeper offense to be had by allowing them in inner classes whose instances are already coupled with outer class instances by design. Forbidding static members within inner classes in favor of the instance-dependent spirit reaps the added bonus of preempting this deeper OOP offense.

On the other hand, no such offense is entailed by static constants, which do not meaningfully constitute state and so these are allowable. Why not forbid static constants for maximum consistency with the instance-dependent spirit? Perhaps because constants need not take up more memory than necessary (if they're forced to be non-static then they’re copied into every inner class instance which is potentially wasteful). Otherwise I can’t imagine the reason for the exception.

It may not be rock-solid reasoning but IMO it makes the most sense of Oracle's cursory remark on the matter.

The work to add records to JDK16 also mentions that static methods and fields can now be used with inner classes, even permitting main() to launch the class.

For example this compiles and runs in JDK16, and can select either main() to be run as java Outer or java Outer$Inner:

public class Outer {
    public static void main(String[] args) {
        System.out.println("Outer class main xxx="+Inner.xxx+" nnn="+(++Inner.nnn)+" iii="+(--iii));
        aaa();
        Inner.zzz();
    }
    public static void aaa() {
        System.out.println("aaa() nnn="+(++Inner.nnn)+" iii="+(--iii));
    }
    public static int iii = 100;

    class Inner {
        public static final String xxx= "yyy";
        public static int nnn = 0;

        public static void zzz() {
            System.out.println("zzz() "+" nnn="+(++nnn)+" iii="+(--iii));
        }
        public static void main(String[] args) {
            System.out.println("Inner class main xxx="+xxx+" nnn="+(++nnn)+" iii="+(--iii));
            zzz();
            aaa();
        }
    }
}

An inner class is something completely different from a static nested class although both are similar in syntax. Static nested classes are only a means for grouping whereas inner classes have a strong association - and access to all values of - their outer class. You should be sure why you want to use an inner class and then it should come pretty natural which one you have to use. If you need to declare a static method it's probably a static nested class you want anyway.

suppose there are two instances of outer class & they both have instantiated inner class.Now if inner class has one static member then it will keep only one copy of that member in heap area.In this case both objects of outer class will refer to this single copy & they can alter it together.This can cause "Dirty read" situation hence to prevent this Java has applied this restriction.Another strong point to support this argument is that java allows final static members here, those whose values can't be changed from either of outer class object. Please do let me if i am wrong.

You are allowed static methods on static nested classes. For example

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}
Related