-2

I have the next code:

    for(int i = 0; i < fileRefernces.size(); i++) { 
        Thread t = new Thread(new Runnable() {
                    public void run() {
                        JLabel pageNumber = new JLabel("<html><font color='#003b86'>PAGE" + (i + 1) + "</font></html>", JLabel.LEFT);
                        JLabel imageLabel = new JLabel(image, JLabel.LEFT);
                        // content would be probably some Image class or byte[]

                        // or:
                        // InputStream in = Loc.openStream();
                        // read image from in
                    }
                });
    }

But, just at the moment to assign the value, I get the next error:

error: local variables referenced from an inner class must be final or effectively final

How I can assign values to those variables?

Benjamin RD
  • 11,516
  • 14
  • 87
  • 157
  • Where does the exception occur? The code you posted works fine normally.. – Yassin Hajaj May 07 '19 at 13:22
  • No @Yassin Hajaj it doesn't work, I posted the error – Benjamin RD May 07 '19 at 13:27
  • so `image` variable is not final/effecitvely final. If you use variables declared outiside of lambda expression scope they have to be final or effectively final. – Michał Krzywański May 07 '19 at 13:30
  • See https://javarevisited.blogspot.com/2015/03/what-is-effectively-final-variable-of.html or similar re. final and effectively final – Brian Agnew May 07 '19 at 13:30
  • This will never work because `i` will never be final – Yassin Hajaj May 07 '19 at 13:34
  • And hint: you really shouldnt create new UI elements within a separate thread. Rather create *all* meaningful UI elements upfront, and then, in your other thread maybe further configure those UI elements, to then make them visible (and keep in mind: you should only make UI updates using SwingWorker resp. "invokeLater()"). No user owned thread should just go in and touch the UI! – GhostCat May 07 '19 at 13:38
  • All Swing methods and constructors need to be called in the AWT event dispatch thread. Creating them in any other thread will lead to unpredictable behavior; meaning, things will sometimes break for no obvious reason. See https://docs.oracle.com/javase/tutorial/uiswing/concurrency/. – VGR May 07 '19 at 13:52

2 Answers2

1

error: local variables referenced from an inner class must be final or effectively final

This compiler error is exactly your problem, because in java, all local variables used inside lambdas or inner classes must be final or effectively final just as the compilation error says.

Basically, this means any local variables defined in your method (not within the inner class) cannot be used unless they are final/effectively final. Take this:


public void someMethod() {
    int x = 5;
    Thread thread = new Thread(new Runnable() {
        public void run() {
            x = 4; // This is a compilation error.
        }
    });
    thread.start();
}

The variable x is not effectively final because the value is changed inside the run method, therefore it cannot be used.

Now take this:

public void someMethod() {
    int x = 5;
    x = 4;
    Thread thread = new Thread(new Runnable() {
        public void run() {
            System.out.println(x); // This is a compilation error.
        }
    });
    thread.start();
}

This also causes a compilation error because even though the value of x is changed outside the run method, it is not effectively final because of that change.

Both of the above examples are with local variables that are not effectively final. Now, here:

public void someMethod() {
    int x = 5;
    final int y = 6;
    List<String> list = new ArrayList<>();
    Thread thread = new Thread(new Runnable() {
        public void run() {
            System.out.println(x);
            System.out.println(y);
            list.add("String added in thread");
            list.forEach(System.out::println); // Will print "String added in thread"
        }
    });
    thread.start();
}

Here, x, y, and list are all valid variables that can be used within an inner class since they are all effectively final or final.

Matt
  • 902
  • 7
  • 20
1

Use local final variables that you'll instantiate once only and it's solved.

final Image finalImage = image;
for(int i = 0; i < fileRefernces.size(); i++) { 
    final int finalCounter = i + 1;
    Thread t = new Thread(new Runnable() {
        public void run() {
            JLabel pageNumber = new JLabel("<html><font color='#003b86'>PAGE" + finalCounter + "</font></html>", JLabel.LEFT);
            JLabel imageLabel = new JLabel(finalImage, JLabel.LEFT);
            // commented code
        }
    });
}
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89