2017-07-04 2 views
1

Je dois créer plusieurs boutons et télécharger des fichiers. Donc, je veux créer une fonction pour définir ces boutons. Cependant, je reçois une erreur de compilation à l'intérieur de mon setNewButton.Java - accès à une variable dans la classe interne ou attribution d'une valeur à la variable finale

Mon code se présente comme suit:

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

L'erreur est: Variable file is accessed from within inner class, needs to be declared final

J'ai vérifié quelques questions similaires à stackoverflow. Je sais file doit être final comme label et fileName ici.

Cependant file ici pourrait être final puisque je voudrais lui assigner selectedFile.

Je voudrais savoir s'il existe une solution de contournement pour ce problème.

Toute aide serait appréciée. :)

Grâce à @M. Prokhorov et @Chang Liu. Selon JLS 8.1.3. Inner Classes and Enclosing Instances

Toute variable locale, paramètre formel, ou exception paramètre utilisé, mais non déclarée dans une classe interne doit soit être déclarée finale ou effectivement finale, ou une erreur de compilation se produit lorsque l'utilisation est tentée .

Alors, quand j'ai essayé d'envoyer un paramètre file intérieur FileSlectionListener, il y aura une erreur de compilation. Cependant, le membre file1 à l'intérieur Solution n'est pas une variable locale, donc si je supprime le file de ma méthode, il n'y aura pas d'erreur. Donc, la réponse de @talex est juste dans ce cas.

Cependant, comme mon problème est de trouver une méthode pour passer un File à la classe interne et affecter la variable avec selectedFile, je ne pouvais pas trouver un moyen pour cela. Ma solution de contournement est basée sur la réponse de @Chang Liu.

Mon code révisé est comme ci-dessous:

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

encore, bienvenue pour me donner des conseils si vous avez une meilleure réponse. :)

+0

Je cherche où 'selectedFile' est déclaré. Où est-ce? – markspace

+0

@markspace C'est un paramètre de la méthode setSelection. – pacifier21

+1

Voir [cette JLS] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3) à propos de votre erreur. Ce dont il s'agit essentiellement - java interdit fortement (via l'erreur de compilation) les classes internes anonymes (y compris lambdas) de "capturer" des variables qui pourraient être réaffectées pendant que l'instance de classe interne est active. –

Répondre

0

Comme il est dit de M. Prokhorov's comment, si vous allez à la JLS 8.1.3. Inner Classes and Enclosing Instances, vous verrez, il est dit que:

Toute variable locale, paramètre formel ou paramètre d'exception utilisé, mais non déclarée dans une classe interne doit soit être déclaré final ou être effectively final, ou une erreur de compilation se produit lorsque l'utilisation est tentée.

Des règles similaires sur l'utilisation de variables s'appliquent dans le corps d'une expression lambda.

Ainsi, le paramètre File file de la méthode setNewButton comme variable n'est pas effectively final dans votre méthode de classe interne new FileSelectionListenersetSelection, à savoir que vous avez attribué une nouvelle valeur à cette variable, ce qui en fait paseffectivement final.

Certaines solution pour résoudre cette erreur de compilation, en définissant un setter pour file au lieu de passer un argument (mais je ne suis pas sûr que ce soit une bonne pratique):

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

Merci. C'est clair pour moi. – Pingping

0

Vous pouvez vous créer une classe wrapper mutable pour le fichier:

public class FileWrapper { 

    /** The file. */ 
    private File file; 

    public File getFile() { 
     return file; 
    } 

    public void setFile(File file) { 
     this.file = file; 
    } 
} 

Ensuite, vous pouvez utiliser une dernière instance de cette classe:

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

et obtenir le dernier fichier sélectionné par appeler fileWrapper.getFile() en dehors de votre classe intérieure.

+2

Vous pouvez inclure 'file' dans un' AtomicReference' pour garantir la sécurité des threads. – mre

+0

Pour compléter cette réponse, il serait bon d'expliquer pourquoi cette erreur est survenue en premier lieu. –

+0

@OH SPIDERS DIEU J'aime votre réponse. Cependant, il y aura un problème. 'setSelection' ne fonctionnera pas util je clique sur le bouton. Cependant, si j'appelle 'fileWrapper.getFile()' en dehors de ma classe interne, il n'attendra pas que la classe interne ait appelé 'setFile()'. En résumé, 'fileWrapper.getFile()' n'obtiendra rien dans ce cas. – Pingping

0

extrayez votre auditeur d'action pour être nommée classe interne de la solution et vous serez en mesure d'assigner Solution.this.file de la classe interne:

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); 
    } 
} 
+0

Merci pour votre réponse. Cependant, c'est un peu déroutant pour moi. – Pingping

+0

J'ai mis à jour la réponse avec l'exemple –

+0

Merci pour votre exemple. – Pingping

2

Vous avez deux variables avec le nom file. L'une est une variable de classe et l'autre est un paramètre de méthode.Il suffit de supprimer le paramètre file de votre méthode et tout fonctionnera correctement.

+0

Non, il ne sera pas, voir mon commentaire sur la question. –

+0

Je tapais juste ça. C'est ABSOLUMENT la bonne réponse! Changez le nom du paramètre method en autre chose que "file" et l'avertissement de compilation disparaît. – pacifier21

+0

@ M.Prokhorov vous êtes rigt sur JLS, mais faux dans ce cas particulier. Cette règle ne s'applique pas aux variables d'instance. – talex