Problemi con ambito variabile locale. Come risolverlo?

Ricevo il seguente errore quando statemet.executeUpdate() ad eseguire statemet.executeUpdate() nel mio codice:

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

Questo è il mio codice finora:

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

Ho anche provato a impostare la statement finale, ma la dichiarazione mi dà un altro errore.

Hai un problema con l’ambito, perché statement è una variabile di metodo locale definita qui:

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

Quando si tenta di accedere a questa variabile nel metodo mouseDown() si sta tentando di accedere a una variabile locale da una class interna anonima e l’ambito non è sufficiente. Quindi deve essere final (che non è ansible fornire il codice) o dichiarato come membro della class in modo che la class interna possa accedere a questa variabile di statement .

fonti:

  • Classi anonime
  • Come sono usate le classi anonime (interne) in Java?

Come risolverlo?

Potresti…

Rendi statement un membro della class anziché di una variabile locale:

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

Potresti…

Definisci un’altra variabile finale e usa invece questa, come suggerito da @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(); } ... } 

Ma dovresti…

Riconsidera il tuo approccio. Se la variabile btnInsert non verrà utilizzata fino a quando btnInsert viene premuto il pulsante btnInsert , non ha senso creare una connessione prima che ciò avvenga effettivamente. Potresti usare tutte le variabili locali come questa:

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

C’è anche una terza soluzione a questo problema oltre a renderla definitiva o dichiarare la variabile inaccessibile come un membro della class poiché queste opzioni non sono sempre possibili o desiderabili.

In primo luogo, NON POSSIAMO semplicemente rendere la variabile finale in quanto il suo stato potrebbe cambiare durante l’esecuzione del programma e le nostre decisioni all’interno della sovrascrittura della class interna potrebbero dipendere dal suo stato corrente.

In secondo luogo, una buona pratica di programmazione orientata agli oggetti suggerisce di utilizzare solo variabili / costanti che sono vitali per la definizione della class come membri della class. Ciò significa che se la variabile a cui facciamo riferimento all’interno della sovrascrittura della class interna anonima è solo una variabile di utilità, non dovrebbe essere elencata tra i membri della class.

Quindi, come di Java 8, ora abbiamo una terza opzione, qui descritta:

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

A partire da Java SE 8, se dichiari la class locale in un metodo, può accedere ai parametri del metodo.

Così ora possiamo semplicemente inserire il codice contenente la nuova class interna e il suo metodo di sovrascrittura in un metodo statico privato i cui parametri includono la variabile chiamata all’interno dell’override. Questo metodo statico viene quindi chiamato dopo l’istruzione dichiarazione btnInsert: –

  // Original code : Button btnInsert = new Button(shell, SWT.NONE); // Call to new private static method : addMouseListener(Button btnInsert, Statement statement); . . . . . . . . . // New private static method to give access to query statement : private static void addMouseListener(Button btn, Statement st) { 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) { // TODO Auto-generated catch block e1.printStackTrace(); } } }); return; } . . . . . . . . . . . . 

non errore:

JSONObject json1 = getJsonX();

Errore:

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

Errore: l’ istruzione delle variabili locali definita in un ambito allegato deve essere definitiva o effettivamente definitiva.

Ma puoi scrivere:

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

Ho trovato questo approccio utile. In questo modo non hai bisogno di un corso o di una finale

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