-2

I have an OuterClass and an NestedClass. Like this.

public class OuterClass
{

        public class NestedClass
        {



        }

}

Whenever I try to call the constructor of NestedClass in a static context (like as a static field or a static method), I get a compiler error.

Here is an example code.

public class OuterClass
{

        public class NestedClass
        {

                public static final NestedClass someStaticField = new NestedClass();

                public static NestedClass someStaticMethod()
                {

                        return new NestedClass();

                }

        }

}

And here are the compiler errors that I get.

$ javac OuterClass.java
OuterClass.java:7: error: non-static variable this cannot be referenced from a static context
                public static final NestedClass someStaticField = new NestedClass();
                                                                 ^
OuterClass.java:12: error: non-static variable this cannot be referenced from a static context
                        return new NestedClass();
                               ^
2 errors

What do these errors mean, and what should I be writing instead to achieve the desired outcome of being able to call the constructor in a static context?

davidalayachew
  • 1,279
  • 1
  • 11
  • 22

2 Answers2

3

The problem is that the NestedClass is not static - neither implicitly nor explicitly declared as such - so it is an inner class. From Java Language Specification 8.1.3. Inner Classes and Enclosing Instances:

An inner class is a nested class that is not explicitly or implicitly static.

To create an instance of such inner class, an instance of the enclosing (outer class) is needed. If not in a static context, this is implicitly used when creating such an instance. In a static context, there is no this and we must provide the instance of the enclosing class. This is accomplish by using a qualified class instance creation expression like:

outer.new InnerClass()

as described in JLS 15.9. Class Instance Creation Expressions:

A qualified class instance creation expression enables the creation of instances of inner member classes and their anonymous subclasses.

More details can be found in JLS 15.9.2. Determining Enclosing Instances.


Applying that to the code of the question, we could do:

public class OuterClass
{

    public class NestedClass // actually an Inner Class
    {
        public static final NestedClass someStaticField = new OuterClass().new NestedClass();

        public static NestedClass someStaticMethod()
        {
            var outer = new OuterClass();
            return outer.new NestedClass();
        }
    }
}

A second possibility would be to not make the nested class an inner class by declaring it as static and avoiding the need for an enclosing instance:

public class OuterClass
{

    // static added to following declaration
    public static class NestedClass
    {
        public static final NestedClass someStaticField = new NestedClass();

        public static NestedClass someStaticMethod()
        {
            return new NestedClass();
        }
    }
}

Which solution to use should be based on the specific use case and data model, not just because the compiler accepts it!


Note: static members of inner classes are allowed since Java 16 (JEP 359), so we can have record classes, which are static, declared in inner classes.

user16320675
  • 135
  • 1
  • 3
  • 9
  • Thank you for the answer! I see now how there always was a way to accomplish this, but I see now that they allowed even more. – davidalayachew Jul 09 '23 at 14:47
-3

Long story short, the problem is because, in Java, if my NestedClass does not have a static modifier up front, then it is considered an Inner Class, and Inner Classes are not allowed to have static fields or methods. So, if I want my NestedClass to have static fields or methods, then I must make my NestedClass a Static Nested Class. Meaning, I need to put a static modifier in front of the class on my NestedClass.

Here is the change.

BEFORE -- public class NestedClass
AFTER  -- public static class NestedClass

And for the long version, here is a great tutorial by Oracle themselves that walks you through the concept.

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

In short, if there is a class B that is inside of class A, then class B is considered a Nested Class.

There are 2 types of Nested Classes -- Inner Classes and Static Nested Classes.

Inner Classes are when the class is defined the way I did above -- one class inside of another. In the immediate example, class B would NOT have a static modifier in front of it.

Static Nested Classes are the exact same as Inner Classes, but they DO have a static modifier in front of it.

So for example.

public class OuterClass
{

        //    not static!
        public class InnerClass
        {

                public static final InnerClass someStaticField = new InnerClass();

                public static InnerClass someStaticMethod()
                {

                        return new InnerClass();

                }

        }
        
        //     static! -- If you don't put the static keyword, then it's not a static class!
        public static class StaticNestedClass
        {

                public static final StaticNestedClass someStaticField = new StaticNestedClass();

                public static StaticNestedClass someStaticMethod()
                {

                        return new StaticNestedClass();

                }


        }

}

If I try to compile this, my InnerClass gets the same 2 compilation errors (like in my question), but my StaticNestedClass gets NO COMPILATION ERRORS!

The reason for this is that Static Nested Classes are allowed to have static fields/methods, but Inner Classes are not allowed to have static fields/methods.

This is a strict rule. That means, the only way I can possibly get my NestedClass to have a static field or method is by turning that Inner Class into a Static Nested Class, and that is done by putting the static keyword in front of the class keyword.

Here is the fixed version.

public class OuterClass
{

        //now its static!
        public static class NestedClass
        {

                public static final NestedClass someStaticField = new NestedClass();

                public static NestedClass someStaticMethod()
                {

                        return new NestedClass();

                }

        }

}

No more compilation errors!

davidalayachew
  • 1,279
  • 1
  • 11
  • 22
  • 1
    "Inner Classes are not allowed to have static fields or methods." This is totally wrong... Did you even try that? Also, to create your instance you can write `public static Inner instance = new Outer().new Inner()`. – Alex R Jul 09 '23 at 07:24
  • 1
    this answer is wrong - see Java Language Specification [8.1.3. Inner Classes and Enclosing Instances](https://docs.oracle.com/javase/specs/jls/se20/html/jls-8.html#jls-8.1.3-130): "*In particular, an inner class may declare and inherit static members (§8.2), and declare static initializers (§8.7), even though the inner class itself is not static.*" (the restriction was *relaxed* in Java 16 - [JEP 395](https://openjdk.org/jeps/395)) – user16320675 Jul 09 '23 at 07:54
  • Hey folks, thanks for the correction. To answer your question @AlexR I absolutely did try, I cycled through several different permutations. But I didn't try that one. Either way, my main mistake was not verifying my logic against the JLS. Thanks again for the help! – davidalayachew Jul 09 '23 at 14:43
  • And thank you for the notice @user16320675 I guess more ways to accomplish this have recently popped up – davidalayachew Jul 09 '23 at 14:51