Проблемы с локальной областью переменных. Как его решить?
Я получаю следующую ошибку при попытке выполнить 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, но объявление дает мне еще одну ошибку.
Ответы
Ответ 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));