Java - 访问内部类中的变量或为最终变量赋值

时间:2022-02-07 22:36:48

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.

我需要创建几个按钮并上传文件。所以我想创建一个设置这些按钮的功能。但是,我在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

错误是:从内部类中访问变量文件,需要声明为final

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

我在*中检查了一些类似的问题。我知道文件必须像label和fileName这样最终。

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

但是这里的文件可能是最终的,因为我想将selectedFile分配给它。

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

感谢@M。普罗霍罗夫和@Chang Liu。根据JLS 8.1.3。内部类和封闭实例

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.

使用但未在内部类中声明的任何局部变量,形式参数或异常参数必须声明为final或有效final,否则在尝试使用时会发生编译时错误。

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.

因此,当我尝试在FileSlectionListener中发送参数文件时,将出现编译错误。但是,Solution里面的成员file1不是局部变量,所以如果我从我的方法中删除文件,就没有错误。所以@talex的anwser在这种情况下是正确的。

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.

但是,由于我的问题是找到一个方法将File传递给内部类并使用selectedFile分配变量,我无法找到它的方法。我的解决方法基于@Chang Liu的回答。

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. :)

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

4 个解决方案

#1


0  

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:

正如M. Prokhorov的评论所述,如果你去JLS 8.1.3。内部类和封闭实例,您将看到它被声明:

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.

使用但未在内部类中声明的任何局部变量,形式参数或异常参数必须声明为final或有效final,否则在尝试使用时会发生编译时错误。

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

关于变量使用的类似规则适用于lambda表达式的主体。

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

因此,方法setNewButton的参数文件文件作为变量在内部类中不是最终的新FileSelectionListener的方法setSelection,即,您为此变量分配了一个新值,这使得它不是最终的。

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):

解决此编译时错误的一些解决方法,通过为文件定义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");
    }
}

#2


2  

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.

只需从您的方法中删除参数文件,一切正常。

#3


0  

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.

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

#4


0  

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 :

只需将您的动作侦听器提取为Solution的内部类,您就可以从内部类中分配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);
    }
}

#1


0  

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:

正如M. Prokhorov的评论所述,如果你去JLS 8.1.3。内部类和封闭实例,您将看到它被声明:

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.

使用但未在内部类中声明的任何局部变量,形式参数或异常参数必须声明为final或有效final,否则在尝试使用时会发生编译时错误。

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

关于变量使用的类似规则适用于lambda表达式的主体。

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

因此,方法setNewButton的参数文件文件作为变量在内部类中不是最终的新FileSelectionListener的方法setSelection,即,您为此变量分配了一个新值,这使得它不是最终的。

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):

解决此编译时错误的一些解决方法,通过为文件定义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");
    }
}

#2


2  

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.

只需从您的方法中删除参数文件,一切正常。

#3


0  

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.

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

#4


0  

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 :

只需将您的动作侦听器提取为Solution的内部类,您就可以从内部类中分配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);
    }
}