Java - 访问内部变量 class 或为最终变量赋值

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

我需要创建几个按钮并上传文件。所以我想创建一个函数来设置这些按钮。但是,我在 setNewButton.

中遇到编译错误

我的代码如下:

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);
    }
}

错误是:Variable file is accessed from within inner class, needs to be declared final

我在Whosebug上查了一些类似的问题。我知道 file 必须像 labelfileName 一样是最终的。

但是这里的 file 可能是 final 因为我想给它分配 selectedFile

我想知道这个问题是否有解决方法。

如有任何帮助,我们将不胜感激。 :)

感谢@M。 Prokhorov 和@Chang Liu。 根据 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.

所以当我试图在FileSlectionListener里面发送一个参数file时,就会出现编译错误。但是,Solution 中的成员 file1 不是局部变量,所以如果我从我的方法中删除 file ,就不会出现错误。所以@talex 的回答在这种情况下是正确的。

然而,由于我的问题是找到一种方法将 File 传递给内部 class 并用 selectedFile 分配变量,我找不到解决方法。我的解决方法是基于@Chang Liu 的回答。

我修改后的代码如下:

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");
        }
    }
}

不过,如果您有更好的答案,欢迎给我任何建议。 :)

您可以为文件自己创建一个可变包装器class:

public class FileWrapper {

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

    public File getFile() {
        return file;
    }

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

然后你可以使用那个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());
        }
    });

并通过在内部 class 之外调用 fileWrapper.getFile() 来获取最后选择的文件。

只需将您的动作侦听器提取为解决方案的内部 class,您就可以从内部 class 分配 Solution.this.file :

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);
    }
}

您有两个名为 file 的变量。一个是 class 变量,另一个是方法参数。

只需从您的方法中删除参数 file,一切都会正常进行。

, if you go to the 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.

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

所以作为变量的方法setNewButton的参数File file在你的内部classnew FileSelectionListener 的方法 setSelection,也就是说,你给这个变量赋了一个新值,这使得它 not effectively final

通过为 file 定义 setter 而不是传递参数来解决此编译时错误的一些解决方法(但我不确定这是否是最佳做法):

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");
    }
}