简体   繁体   中英

Java - access to variable within inner class or assign a value to final variable

I need to create several buttons and upload files. So I want to create a function to set these buttons. However, I'm getting a compilation error inside of my setNewButton .

My code is shown as below:

public class Solution extends JFrame {
    private static final String FILE_NAME_1 = "my file1";
    private File file1;
    private void setNewButton(Container contentPane, final String fileName, String format, File file) {
        contentPane.add(Box.createVerticalStrut(5));
        final Label label = new Label("Select " + fileName + " in ." + format +" format");
        contentPane.add(label);
        contentPane.add(Box.createVerticalStrut(10));
        Button selection = new Button("Select " + fileName);
        contentPane.add(selection);
        selection.addActionListener(new FileSelectionListener("Only " + format + " is allowed", format) {
            @Override
            protected void setSelection(File selectedFile) {
                file = selectedFile;  // compilation error here
                label.setText("Selected" + fileName + selectedFile.getAbsolutePath());
            }
        });
    }

    public uploadFiles() {
        Container contentPane = this.getContentPane();
        setNewButton(contentPane, FILE_NAME_1, "xls", file1);
    }
}

The error is: Variable file is accessed from within inner class, needs to be declared final

I have checked some similar questions in stackoverflow. I know file has to be final like label and fileName here.

However file here could be final since I would like to assign selectedFile to it.

I would like to know if there is any walkaround for this problem.

Any help would be appreciated. :)

Thanks to @M. Prokhorov and @Chang Liu. According to JLS 8.1.3. Inner Classes and Enclosing Instances

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be effectively final, or a compile-time error occurs where the use is attempted.

So when I tried to send a parameter file inside FileSlectionListener , there will be a compile error. However, the member file1 inside Solution is not local variable, so if I delete the file from my method, there will be no error. So @talex 's anwser is right in this case.

However since my problem is to find a method to pass a File to inner class and assign the variable with selectedFile , I couldn't find a way for it. My workaround is based on @Chang Liu's answer.

My revised code is as below:

public class Solution extends JFrame {
    private static final String FILE_NAME_1 = "my file1";
    private File file1;
    private void setNewButton(Container contentPane, final String fileName, String format) {
        contentPane.add(Box.createVerticalStrut(5));
        final Label label = new Label("Select " + fileName + " in ." + format +" format");
        contentPane.add(label);
        contentPane.add(Box.createVerticalStrut(10));
        Button selection = new Button("Select " + fileName);
        contentPane.add(selection);
        selection.addActionListener(new FileSelectionListener("Only " + format + " is allowed", format) {
            @Override
            protected void setSelection(File selectedFile) {
                setFile(selectedFile, fileName);  // no compilation error here
                label.setText("Selected" + fileName + selectedFile.getAbsolutePath());
            }
        });
    }

    public uploadFiles() {
        Container contentPane = this.getContentPane();
        setNewButton(contentPane, FILE_NAME_1, "xls", file1);
    }

    private void setFile(File file, String fileName) {
        switch (fileName) {
            case FILE_NAME_1:
                sollFile = file;
                break;
            default:
                throw new AssertionError("Unknown File");
        }
    }
}

Still, welcome to give me any advise if you have a better answer. :)

You have two variables with name file . One is class variable and another is method parameter.

Just remove parameter file from your method and all will work fine.

You can create yourself a mutable wrapper class for the file:

public class FileWrapper {

    /** The file. */
    private File file;

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }
}

Then you can use a final instance of that class:

final private FileWrapper fileWrapper = new FileWrapper();

// ...     

selection.addActionListener(new FileSelectionListener("Only " + format + " is allowed", format) {
        @Override
        protected void setSelection(File selectedFile) {
            fileWrapper.setFile(selectedFile);  
            label.setText("Selected" + fileName + selectedFile.getAbsolutePath());
        }
    });

And get the last selected file by calling fileWrapper.getFile() outside of your inner class.

Just extract your action listener to be named inner class of Solution and you will be able to assign Solution.this.file from the inner class :

public class Solution extends JFrame {
   private class MyListener extends FileSelectionListener{
@Override
            protected void setSelection(File selectedFile) {
                Solution.this.file = selectedFile;  // NO compilation error here
            }
}
    private static final String FILE_NAME_1 = "Selected SOLL:";
    private File file;
    private void setNewButton(Container contentPane, final String fileName, String format, File file) {
        contentPane.add(Box.createVerticalStrut(5));
        final Label label = new Label("Select " + fileName + " in ." + format +" format");
        contentPane.add(label);
        contentPane.add(Box.createVerticalStrut(10));
        Button selection = new Button("Select " + fileName);
        contentPane.add(selection);
        selection.addActionListener(new MyListener() );
    }

    public uploadFiles() {
        Container contentPane = this.getContentPane();
        setNewButton(contentPane, FILE_NAME_1, "xls", file1);
    }
}

As stated from M. Prokhorov's comment , if you go to the JLS 8.1.3. Inner Classes and Enclosing Instances , you will see it is stated that:

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be effectively final , or a compile-time error occurs where the use is attempted.

Similar rules on variable use apply in the body of a lambda expression.

So the parameter File file of method setNewButton as a variable is not effectively final in your inner class new FileSelectionListener 's method setSelection , ie, you assigned a new value to this variable, which makes it not effectively final .

Some workaround to resolve this compile-time error, by defining a setter for file instead of passing an argument (but I am not sure whether this is a best practice):

public class Solution extends JFrame {
    private static final String FILE_NAME_1 = "Selected SOLL:";
    private File file;
    private void setNewButton(Container contentPane, final String fileName, String format) {
        contentPane.add(Box.createVerticalStrut(5));
        final Label label = new Label("Select " + fileName + " in ." + format +" format");
        contentPane.add(label);
        contentPane.add(Box.createVerticalStrut(10));
        Button selection = new Button("Select " + fileName);
        contentPane.add(selection);
        selection.addActionListener(new FileSelectionListener("Only " + format + " is allowed", format) {
            @Override
            protected void setSelection(File selectedFile) {
                setFile(selectedFile);  // call file setter here
                label.setText("Selected" + fileName + selectedFile.getAbsolutePath());
            }
        });
    }

    // define a setter for your File member
    private void setFile(File file) {
        this.file = file;
    }

    public void uploadFiles() {
        Container contentPane = this.getContentPane();
        setNewButton(contentPane, FILE_NAME_1, "xls");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM