2
votes

I am using eclipse and windows builder --> Swing --> Jframe. I have added textField, lblNewLabel and btnNewButton. By clicking and dropping it on contentPane. When trying to assign a value to a lblNewLabelusing methods lblNewLabel.setText(textField.getText());. I get the following error: Cannot refer to a non-final variable lblNewLabel inside an inner class defined in a different method.

This is my source code:

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.JTextField;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;


public class MultAppMed extends JFrame {

    private JPanel contentPane;
    private JTextField textField;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    MultAppMed frame = new MultAppMed();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public MultAppMed() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        JLabel lblNewLabel = new JLabel("New label");
        lblNewLabel.setBounds(106, 14, 46, 14);
        contentPane.add(lblNewLabel);

        JButton btnNewButton = new JButton("New button");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                lblNewLabel.setText(textField.getText());
            }
        });
        btnNewButton.setBounds(335, 228, 89, 23);
        contentPane.add(btnNewButton);

        textField = new JTextField();
        textField.setBounds(10, 11, 86, 20);
        contentPane.add(textField);
        textField.setColumns(10);
    }

}

Why is this happening? I was reading some answers that say: "Java requires references to variables from inner classes to be final variables" but if needed why doesn't Jframe insert it as such automatically. My textField is working and I can get and set Its value using the same methods. I was meaning to ask why and how to solve this. I appreciate your help.

2
It sounds like you already have a solution to your problem. What is your question?Keppil
My question is why do i have to tweak the code to use a simple example of assigning a value to a label. Given that i don't need to do that for a text field. Is it a bug or a feature. And how to do it, what is the best way to achieve that.user3747557
what don't you need to do for a text field? Either make lblNewLabel final or make it instance member. What you want to achieve?Braj
Just make your variable final, for the reason stated in the answers you have read. It is required by design in Java, so it is not a bug. Note that from Java 8, this requirement is not as strict. There Java only requires that the variable is effectively final, i.e. not changed after initialization.Keppil
@eror404: To understand WHY, might be this example, of mine will help you in the direction :-)nIcE cOw

2 Answers

3
votes

You most probably want to change the settings of the window builder with this option "Declare variable as final" on local varaibles.

enter image description here

This should do the trick, since "... . However, a local class can only access local variables that are declared final."

Alternative with fields

Consider using the "convert to local field" operation inside the window builder.

enter image description here

This will change the definition of the label from

JLabel lblNewLabel = new JLabel("New label");

into

private JLabel lblNewLabel;
// in initialization method / c'tor
lblNewLabel = new JLabel("New label");

Environment

Everything tested using

  • eclipse Version: Kepler Service Release 1 | Build id: 20130919-0819
  • window builder Version: 1.6.1 | Build id: 2013.09.10

eclipse.ini

-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.200.v20130807-1835
-product
org.eclipse.epp.package.standard.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.6
-Xms40m
-Xmx512m

java version

C:\....\isi>java -version
java version "1.6.0_30"
Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
Java HotSpot(TM) 64-Bit Server VM (build 20.5-b03, mixed mode)
1
votes

My question is why do i have to tweak the code to use a simple example of assigning a value to a label. Given that i don't need to do that for a text field

lblNewLabel is a local variable that can't be accessed inside the anonymous inner class until and unless it's final.

JLabel lblNewLabel = new JLabel("New label");

where as textField is instance member that can be accessed easily inside the anonymous inner class.

private JTextField textField;

Inner class and local variables

Local variables always live on the stack, the moment method is over all local variables are gone.

But your inner class objects might be on heap even after the method is over (Say an instance variable holds on to the reference), so in that case it cannot access your local variables since they are gone, unless you mark them as final.

The local variable could change before the inner class accesses it. Making it final prevents that because final variable can't be changed once assigned.