Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Local variable cannot be referenced from static context of local class

I understand that static method cannot access instance variable.
But why static method of a local class cannot access a local variable of the enclosing block?

public class Sequence {
    public static void main(String... args) {
        String message = "Bye bye";
        class EnglishGoodbye {
            public static void sayGoodbye() { 
                System.out.println(message); // Error: non-static variable message 
                                             // cannot be referenced in static context
            }
        }
        EnglishGoodbye.sayGoodbye();
    }
}

The code is from Why is a static local class not allowed in a method?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The ‘mental model’ you need to use to grok this, is that there is one sayGoodbye message. Just the one. It can be invoked at any time, from anywhere.

In contrast, there are many invocations of your main method.

It could have been invoked never. It could have been invoked 5 times, from 5 simultaneous threads.

So, what should X.sayGoodbye() print if main has never been invoked yet? What should it print if each of the 5 concurrently running main methods all have a different value for message?

NB:

Due to the nature of your example, lots of things might confuse.

Yes, of course, main – how could that never have been invoked? But the compiler itself does not treat your main method as magical. It’s not going to allow you to access local vars solely if the method we are in is literally psv main, and, you can invoke your own main method if you want. So it feels like "main" is always invoked at least once and exactly once but that is not actually true.

And, from the code, yes, of course, whatever message we are talking about, its value is clearly always "Bye bye";. However, javac, again, doesn’t take that into consideration. You declared it, you can modify it if you want. The fact that you haven’t isn’t something javac is going to take into account.

The fact that the only place you invoke that static method is a place where message exists, isn’t taken into account. Why should it?

The weirdest ‘yeah… but…’ in this snippet of code is that no code can even call your static method, other than code inside your own main method where therefore there is a clear, single ‘version’ of your message variable. However, that’s.. not how it works. Until recently, putting static methods in method-local classes was flat out illegal. It was recently changed but in general it’s still bizarre to want to do it, and its purpose is to stick utility methods that do not need context into your classes.

Because, let’s take a step back: Why? Why did you make that method static?

Perhaps you were thinking: "For efficiency" – a non-static method is duplicated every time the method is invoked, that’s inefficient. This is false. In the ‘duplication’ sense, all methods are static. The JVM does not, ever, load a method into memory twice. Instead it applies dynamic dispatch were relevant, and at the JVM level the object itself (this) is the first hidden argument. In contrast to non-static fields which really are cloned for every instance. Hence, "for efficiency" is not a reason to make that method static.

The only reason to make it static is to have compiler-checked documentation indicating that the method is separate from its context and operates without needing it. In other words, the whole point of static is to tell the compiler that you do not intend to use any context. Including that variable.

It is technically possible to end up with a reflective access point to invoke that method when no main method is running. Here, highly convoluted:

class Example {
  public static void main(String[] args) {
    String x = Math.random() ? 5 ? "red" : "green";
    static class Whatever {
      public static void foo() { System.out.println(x); }
    }

    final Class<?> ref = Whatever.class;
    new Thread(() -> {
      try {
        Thread.sleep(1000L);
        Method m = ref.getDeclaredMethod("foo");
        m.invoke(null);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }).start();
  }
}

This ridiculous code, if java worked like you think it should, explodes. As in, core dumps. Because there is no x at all.

main has ended. main has made a thread, started it, and finished. The JVM continues because there is at least one non-daemon thread still actively running (it is currently sleeping for 1 second). When that thread is done waiting out its 1 second, it will invoke that static method.

Which is referring to variable x and x no longer exists. Like all local vars, it has poofed out of existence when the method that declared it ended. local vars are declared ‘on the stack’ and thus truly they are definitely gone. Long ago overwritten. Absolutely not memory space you can touch, and even if you could, it’s not there anymore.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading