Подтвердить что ты не робот

Проблемы с локальной областью переменных. Как его решить?

Я получаю следующую ошибку при попытке выполнить statemet.executeUpdate() в моем коде:

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

Это мой код:

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

    }
}

Я также попытался установить statement final, но объявление дает мне еще одну ошибку.

4b9b3361

Ответ 1

У вас действительно проблема с областью видимости, потому что 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();
            }
            ...
    });
}

Когда вы пытаетесь получить доступ к этой переменной внутри метода mouseDown(), вы пытаетесь получить доступ к локальной переменной изнутри анонимного внутреннего класса, а область действия недостаточно. Поэтому определенно должен быть final (который дал вам код невозможен) или объявлен как член класса, поэтому внутренний класс может получить доступ к этой переменной statement.

Источники:


Как его решить?

Вы могли бы...

Сделайте statement членом класса вместо локальной переменной:

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

Вы могли бы...

Определите другую конечную переменную и используйте ее вместо нее, как это было предложено @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();
    }
    ...
}

Но вы должны...

Пересмотреть свой подход. Если переменная 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();
       }
});

Ответ 2

Во-первых, мы просто НЕ МОЖЕМ сделать переменную окончательной, так как ее состояние может изменяться во время выполнения программы, и наши решения в переопределении внутреннего класса могут зависеть от ее текущего состояния.

Во-вторых, хорошая практика объектно-ориентированного программирования предлагает использовать в качестве членов класса только переменные/константы, которые имеют жизненно важное значение для определения класса. Это означает, что если переменная, на которую мы ссылаемся в пределах анонимного внутреннего переопределения класса, является просто служебной переменной, то она не должна быть указана среди членов класса.

Но - с Java 8 - у нас есть третий вариант, описанный здесь:

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

Начиная с Java SE 8, если вы объявляете локальный класс в методе, он может получить доступ к параметрам метода.

Так что теперь мы можем просто поместить код, содержащий новый внутренний класс и переопределение его метода, в закрытый метод, параметры которого включают переменную, которую мы вызываем из переопределения. Этот статический метод затем вызывается после оператора объявления 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;
}

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

Ответ 3

not Error:

JSONObject json1 = getJsonX();

Ошибка:

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

Ошибка: Локальная инструкция переменной, определенная в охватывающей области, должна быть окончательной или окончательной.

Но вы можете написать:

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

Ответ 4

Я нашел этот подход полезным. Таким образом, вам не нужен класс и окончательный

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