'"Variable 'eye' is accessed from within inner class, needs to be final or effectively final"

Alright, so I'm trying to check if a Boolean is True or False and I've initialized it with "true", and then I change it to "false" later. That wouldn't cause problems, right? Well, there is one problem: The code to check that is being run within a thread. Currently this is how I'm doing this:

Boolean eye = true;
        Thread outputSomeInfo = new Thread(new Runnable(){
            public void run(){
                    String gameFrameTitle = gameFrame.getTitle();
                    String gameFrameSize = String.valueOf(gameFrame.getSize());
                    String doorSize = String.valueOf(doorImage.getSize());
                    String doorLocation = String.valueOf(doorImage.getLocation());
                    String gameFrameIcon = String.valueOf(iconURL);
                    String actualGameFrameIcon = gameFrameIcon.substring(gameFrameIcon.indexOf("/")+1);
                    actualGameFrameIcon.trim();
                    Boolean threadEye = true;
                    System.out.println("JFrame Title: " + gameFrameTitle);
                    System.out.println("JFrame Icon: " + actualGameFrameIcon);
                    System.out.println("JFrame Size: " + gameFrameSize);
                    System.out.println("Door Size: " + doorSize);
                    System.out.println("Door Location: " + doorLocation);
                    while (true) {
                        if (!gameFrameTitle.equalsIgnoreCase(gameFrame.getTitle())) {
                            gameFrameTitle = gameFrame.getTitle();
                            System.out.println("JFrame Title: " + gameFrameTitle);
                        }
                        if (threadEye && !eye) {
                            threadEye = false;
                            String gameFrameIcon2 = String.valueOf(doorLoc);
                            String actualGameFrameIcon2 = gameFrameIcon2.substring(gameFrameIcon2.indexOf("/")+1);
                            actualGameFrameIcon2.trim();
                            actualGameFrameIcon = actualGameFrameIcon2;
                            gameFrameIcon = gameFrameIcon2;
                            System.out.println("JFrame Icon: " + actualGameFrameIcon);
                        }
                        if (!gameFrameSize.equalsIgnoreCase(String.valueOf(gameFrame.getSize()))) {
                            gameFrameSize = String.valueOf(gameFrame.getSize());
                            System.out.println("JFrame Size: " + gameFrameSize);
                        }
                        if (!doorSize.equalsIgnoreCase(String.valueOf(doorImage.getSize()))) {
                            doorSize = String.valueOf(doorImage.getSize());
                            System.out.println("Door Size: " + doorSize);
                        }
                        if (!doorLocation.equalsIgnoreCase(String.valueOf(doorImage.getLocation()))) {
                            doorLocation = String.valueOf(doorImage.getLocation());
                            System.out.println("Door Location: " + doorLocation);
                        }
                        // System.out.println("JFrame Size: " + gameFrame.getSize() + "\nJFrame Title: " + gameFrame.getTitle() + "\nDoor Location: " + doorImage.getLocation() + "\nDoor Size: " + doorImage.getSize());
                    }
            }
        });
        outputSomeInfo.start();

(Anything used within this thread that isn't also defined in the thread was defined before defining the thread. "eye" is changed after defining the thread, but that shouldn't matter since the thread runs forever.)

What I expect:

A program that tells me the path to the program's current icon when it changes (icon changes only once and there are only 2 possible icons, always being changed in the same order so it shouldn't matter that it's hardcoded?)

What I get:

Main.java:52: error: local variables referenced from an inner class must be final or effectively final
                        if (threadEye && !eye) {
                                          ^
1 error
error: compilation failed

From what I can tell, what I need is a way to use non-final variables within threads?



Solution 1:[1]

I got it! Fixed code:

final AtomicBoolean eye = new AtomicBoolean();
        eye.set(true);
        Thread outputSomeInfo = new Thread(new Runnable(){
            public void run(){
                    String gameFrameTitle = gameFrame.getTitle();
                    String gameFrameSize = String.valueOf(gameFrame.getSize());
                    String doorSize = String.valueOf(doorImage.getSize());
                    String doorLocation = String.valueOf(doorImage.getLocation());
                    String gameFrameIcon = String.valueOf(doorLoc);
                    String actualGameFrameIcon = gameFrameIcon.substring(gameFrameIcon.indexOf("/")+1);
                    actualGameFrameIcon.trim();
                    Boolean threadEye = true;
                    System.out.println("JFrame Title: " + gameFrameTitle);
                    System.out.println("JFrame Icon: " + actualGameFrameIcon);
                    System.out.println("JFrame Size: " + gameFrameSize);
                    System.out.println("Door Size: " + doorSize);
                    System.out.println("Door Location: " + doorLocation);
                    while (true) {
                        String eyeToString = String.valueOf(eye);
                        if (!gameFrameTitle.equalsIgnoreCase(gameFrame.getTitle())) {
                            gameFrameTitle = gameFrame.getTitle();
                            System.out.println("JFrame Title: " + gameFrameTitle);
                        }
                        if (String.valueOf(eye.get()).equalsIgnoreCase("false")) {
                            if (threadEye) {
                                threadEye = false;
                                String gameFrameIcon2 = String.valueOf(iconURL);
                                String actualGameFrameIcon2 = gameFrameIcon2.substring(gameFrameIcon2.indexOf("/") + 1);
                                actualGameFrameIcon2.trim();
                                actualGameFrameIcon = actualGameFrameIcon2;
                                gameFrameIcon = gameFrameIcon2;
                                System.out.println("JFrame Icon: " + actualGameFrameIcon2);
                            }
                        }
                        if (!gameFrameSize.equalsIgnoreCase(String.valueOf(gameFrame.getSize()))) {
                            gameFrameSize = String.valueOf(gameFrame.getSize());
                            System.out.println("JFrame Size: " + gameFrameSize);
                        }
                        if (!doorSize.equalsIgnoreCase(String.valueOf(doorImage.getSize()))) {
                            doorSize = String.valueOf(doorImage.getSize());
                            System.out.println("Door Size: " + doorSize);
                        }
                        if (!doorLocation.equalsIgnoreCase(String.valueOf(doorImage.getLocation()))) {
                            doorLocation = String.valueOf(doorImage.getLocation());
                            System.out.println("Door Location: " + doorLocation);
                        }
                        // System.out.println("JFrame Size: " + gameFrame.getSize() + "\nJFrame Title: " + gameFrame.getTitle() + "\nDoor Location: " + doorImage.getLocation() + "\nDoor Size: " + doorImage.getSize());
                    }
            }
        });
        outputSomeInfo.start();

Solution 2:[2]

You can use below code to initialize array and use eye[0] when you want value of the variable.

final Boolean[] eye = {true};

OR use eye.get() using below code

AtomicReference<Boolean> eye = new AtomicReference<>(true);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 elias
Solution 2 Sanjaya Deshapriya