Java 局部变量作用域的问题。如何解决?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25894509/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 01:25:36  来源:igfitidea点击:

Problems with local variable scope. How to solve it?

javascope

提问by Amit Chahar

I'm getting the following error when trying to execute statemet.executeUpdate()in my code:

尝试statemet.executeUpdate()在我的代码中执行时出现以下错误:

Local variable statement defined in an enclosing scope must be final or effectively final.

This is my code so far:

到目前为止,这是我的代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;.

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class a1 {

    protected Shell shell;
    private Text text;
    private Text text_1;
    private Text text_2;
    private Text text_3;

    /**
     * Launch the application.
     * @param args
     */
    public static void main(String[] args) {
        try {
            a1 window = new a1();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Open the window.
     */
    public void open() {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /**
     * Create contents of the window.
     */
    protected void createContents() {

        Connection connect = null;

        ResultSet resultSet = null;

        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            connect = DriverManager.getConnection("jdbc:mysql://localhost/railwaydb", "root", "");
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        Statement statement = null;
        // statements allow to issue SQL queries to the database
        try {
            statement = connect.createStatement();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        shell = new Shell();
        shell.setSize(450, 300);
        shell.setText("SWT Application");

        Label lblName = new Label(shell, SWT.NONE);
        lblName.setBounds(10, 43, 47, 15);
        lblName.setText("Name");

        Label lblFrom = new Label(shell, SWT.NONE);
        lblFrom.setBounds(10, 74, 55, 15);
        lblFrom.setText("From");

        Label lblTo = new Label(shell, SWT.NONE);
        lblTo.setBounds(10, 105, 55, 15);
        lblTo.setText("To");

        Label lblPrice = new Label(shell, SWT.NONE);
        lblPrice.setBounds(10, 137, 55, 15);
        lblPrice.setText("Price");

        text = new Text(shell, SWT.BORDER);
        text.setBounds(64, 43, 76, 21);

        text_1 = new Text(shell, SWT.BORDER);
        text_1.setBounds(64, 74, 76, 21);

        text_2 = new Text(shell, SWT.BORDER);
        text_2.setBounds(64, 105, 76, 21);

        text_3 = new Text(shell, SWT.BORDER);
        text_3.setBounds(64, 137, 76, 21);

        Label lblRailwayDatabase = new Label(shell, SWT.NONE);
        lblRailwayDatabase.setBounds(174, 10, 97, 15);
        lblRailwayDatabase.setText("Railway Database");

        Label lblCreateView = new Label(shell, SWT.NONE);
        lblCreateView.setBounds(189, 43, 76, 15);
        lblCreateView.setText("Create View");

        Button btnName = new Button(shell, SWT.CHECK);
        btnName.setBounds(189, 73, 93, 16);
        btnName.setText("Name");

        Button btnFrom = new Button(shell, SWT.CHECK);
        btnFrom.setBounds(189, 105, 93, 16);
        btnFrom.setText("From");

        Button btnTo = new Button(shell, SWT.CHECK);
        btnTo.setBounds(189, 137, 93, 16);
        btnTo.setText("To");

        Button btnPrice = new Button(shell, SWT.CHECK);
        btnPrice.setBounds(189, 171, 93, 16);
        btnPrice.setText("Price");

        Button btnInsert = new Button(shell, SWT.NONE);
        btnInsert.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                String name = text.getText();
                String from = text_1.getText();
                String to = text_2.getText();
                String price = text_3.getText();

                String query = "INSERT INTO booking (name, fromst, tost, price) VALUES ('"+name+"', '"+from+"', '"+to+"', '"+price+"')";
                try {
                    statement.executeUpdate(query);
                } catch (SQLException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        });
        btnInsert.setBounds(10, 171, 75, 25);
        btnInsert.setText("Insert");

        Button btnView = new Button(shell, SWT.NONE);
        btnView.setBounds(307, 74, 75, 25);
        btnView.setText("View");

        Button btnIndex = new Button(shell, SWT.NONE);
        btnIndex.setBounds(307, 127, 75, 25);
        btnIndex.setText("Index");

    }
}

I also tried to set statementfinal but the declaration gives me another error.

我也尝试设置statementfinal 但声明给了我另一个错误。

采纳答案by dic19

You have a scope problem indeed, because statementis a local method variable defined here:

你确实有一个范围问题,因为statement这里定义了一个本地方法变量:

protected void createContents() {
    ...
    Statement statement = null; // local variable
    ...
     btnInsert.addMouseListener(new MouseAdapter() { // anonymous inner class
        @Override
        public void mouseDown(MouseEvent e) {
            ...
            try {
                statement.executeUpdate(query); // local variable out of scope here
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            ...
    });
}

When you try to access this variable inside mouseDown()method you are trying to access a local variable from within an anonymous inner class and the scope is not enough. So it definitely must be final(which given your code is not possible) or declared as a class member so the inner class can access this statementvariable.

当您尝试在mouseDown()方法内部访问此变量时,您是在尝试从匿名内部类中访问局部变量,而范围是不够的。所以它绝对必须是final(鉴于您的代码是不可能的)或声明为类成员,以便内部类可以访问此statement变量。

Sources:

资料来源:



How to solve it?

如何解决?

You could...

你可以...

Make statementa class member instead of a local variable:

创建statement一个类成员而不是局部变量:

public class A1 { // Note Java Code Convention, also class name should be meaningful   
    private Statement statement;
    ...
}

You could...

你可以...

Define another final variable and use this one instead, as suggested by @HotLicks:

按照@HotLicks 的建议,定义另一个最终变量并使用这个变量:

protected void createContents() {
    ...
    Statement statement = null;
    try {
        statement = connect.createStatement();
        final Statement innerStatement = statement;
    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    ...
}

But you should...

但是你应该...

Reconsider your approach. If statementvariable won't be used until btnInsertbutton is pressed then it doesn't make sense to create a connection beforethis actually happens. You could use all local variables like this:

重新考虑你的方法。如果在按下按钮statement之前不会使用变量,btnInsert那么实际发生之前创建连接是没有意义的。您可以像这样使用所有局部变量:

btnInsert.addMouseListener(new MouseAdapter() {
   @Override
   public void mouseDown(MouseEvent e) {
       try {
           Class.forName("com.mysql.jdbc.Driver");
           try (Connection connect = DriverManager.getConnection(...);
                Statement statement = connect.createStatement()) {

                // execute the statement here

           } catch (SQLException ex) {
               ex.printStackTrace();
           }

       } catch (ClassNotFoundException ex) {
           e.printStackTrace();
       }
});

回答by Trunk

Firstly, we just CAN'T make the variable final as its state may be changing during the run of the program and our decisions within the inner class override may depend on its current state.

首先,我们不能将变量设为 final,因为它的状态在程序运行期间可能会发生变化,而我们在内部类覆盖中的决定可能取决于其当前状态。

Secondly, good object-oriented programming practice suggests using only variables/constants that are vital to the class definition as class members. This means that if the variable we are referencing within the anonymous inner class override is just a utility variable, then it should not be listed amongst the class members.

其次,良好的面向对象编程实践建议仅使用对类定义至关重要的变量/常量作为类成员。这意味着如果我们在匿名内部类覆盖中引用的变量只是一个实用程序变量,那么它不应该被列在类成员中。

But – as of Java 8 – we have a third option, described here :

但是 - 从 Java 8 开始 - 我们有第三种选择,描述如下:

https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html

https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html

Starting in Java SE 8, if you declare the local class in a method, it can access the method's parameters.

从 Java SE 8 开始,如果您在方法中声明本地类,则它可以访问该方法的参数。

So now we can simply put the code containing the new inner class & its method override into a private method whose parameters include the variable we call from inside the override. This static method is then called after the btnInsert declaration statement :-

所以现在我们可以简单地将包含新内部类及其方法覆盖的代码放入私有方法中,该方法的参数包括我们从覆盖内部调用的变量。然后在 btnInsert 声明语句之后调用此静态方法:-

 . . . .
 . . . .

 Statement statement = null;                                 

 . . . .
 . . . .

 Button btnInsert = new Button(shell, SWT.NONE);
 addMouseListener(Button btnInsert, Statement statement);    // Call new private method

 . . . 
 . . .
 . . . 

 private static void addMouseListener(Button btn, Statement st) // New private method giving access to statement 
 {
    btn.addMouseListener(new MouseAdapter() 
    {
      @Override
      public void mouseDown(MouseEvent e) 
      {
        String name = text.getText();
        String from = text_1.getText();
        String to = text_2.getText();
        String price = text_3.getText();
        String query = "INSERT INTO booking (name, fromst, tost,price) VALUES ('"+name+"', '"+from+"', '"+to+"', '"+price+"')";
        try 
        {
            st.executeUpdate(query);
        } 
        catch (SQLException e1) 
        {
            e1.printStackTrace();                                    // TODO Auto-generated catch block
        }
    }
  });
  return;
}

 . . . .
 . . . .
 . . . .

回答by mehdi salartayefeh

not Error:

不是错误:

JSONObject json1 = getJsonX();

Error:

JSONObject json1 = getJsonX();

错误:

JSONObject json2 = null;
if(x == y)
   json2 = getJSONX();

Error: Local variable statement defined in an enclosing scope must be final or effectively final.

错误:在封闭作用域中定义的局部变量语句必须是最终的或有效的最终。

But you can write:

但是你可以写:

JSONObject json2 = (x == y) ? json2 = getJSONX() : null;

回答by user4212919

I found this approach useful. This way you do not need a class nor final

我发现这种方法很有用。这样你就不需要班级或决赛

 btnInsert.addMouseListener(new MouseAdapter() {
        private Statement _statement;

        public MouseAdapter setStatement(Statement _stmnt)
        {
            _statement = _stmnt;
            return this;
        }
        @Override
        public void mouseDown(MouseEvent e) {
            String name = text.getText();
            String from = text_1.getText();
            String to = text_2.getText();
            String price = text_3.getText();

            String query = "INSERT INTO booking (name, fromst, tost, price) VALUES ('"+name+"', '"+from+"', '"+to+"', '"+price+"')";
            try {
                _statement.executeUpdate(query);
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    }.setStatement(statement));