Hello,

We are trying to develop a chat client program in a computer science class, but we are running into this error

Invalid reference in inner class to a non-final local variable

. The chat client will send the users' text to a text file and then read depending on a session ID.

This is the exact error: Invalid reference in inner class "Irc$1" to a non-final local variable, "jt", declared in method "main".

I will take you through what we are trying to achive. Here are the specific spots in our code that we are looking at:

1. Starts with the input text area.

// Input text area
        JTextArea jt = new JTextArea (7, 50);
        frame.getContentPane ().add (panel);
        jt.setLineWrap (true);
        jt.setWrapStyleWord (true);
        JScrollPane sbtText;
        sbtText = new JScrollPane (jt);
        sbtText.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        panel.add (sbtText);

2. User hits send button and the text in the textarea is converted into a string and sent to the sendString method.

// Send Button
        JButton btnSend; // Send Message
        btnSend = new JButton ("SEND");
        btnSend.addActionListener (
                new ActionListener ()
        {
            public void actionPerformed (ActionEvent e)
            {
                String s = jt.getText ();
                if (!s.equals (""))
                {
                    jt.selectAll ();
                    // Send the string
                    sendString (s);
                }
            }
        }
        );
        panel.add (btnSend);

3. The sendString method uses a class called writer to write the text to a text file. This will then be read and outputed into the output textarea later. (We haven't gotten that far :p)

private static void sendString (String s)
    {
        Writer w;
        w = new Writer (ta);
        w.Writer (jt, session);
    }

4. I tried putting this in too, just to see if it would do anything.

// Various GUI components and info
    public static JFrame frame = null;
    public final static JTextArea jt = null;
    public final static JTextArea ta = null;

Here is the entire code:

// The "Irc" class.
import java.awt.*;
import java.awt.event.*;
import hsa.Console;
import java.util.Date;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Color;
import javax.swing.JTextArea;
import javax.swing.text.JTextComponent;




public class Irc
{
    // Various GUI components and info
    public static JFrame frame = null;
    public final static JTextArea jt = null;
    public final static JTextArea ta = null;

    static Console c;           // The output console
    public static void main (String[] args) throws IOException
    {
        c = new Console (40, 100);
        int session;
        String output = "This is the test output";
        // Jframe title
        JFrame frame = new JFrame ("Chat Client");
        // Output text area
        String text = "TEST TEXT A JTextArea object represents a multiline area for displaying text. "
            + "You can change the number of lines that can be displayed at a time, "
            + "as well as the number of columns. You can wrap lines and words too. "
            + "You can also put your JTextArea in a JScrollPane to make it scrollable.dsgdsg"
            + "sdgsdgggggggggggggggggggggggggggggggg";
        // Declare a new JPanel to put the text boxes on
        JPanel panel = new JPanel ();
        //Output text area
        // Create a scrollable text area
        JTextArea ta = new JTextArea (text, 15, 50);
        ta.setLineWrap (true);
        JScrollPane sbrText;
        sbrText = new JScrollPane (ta);
        sbrText.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        panel.add (sbrText);
        // Input text area
        JTextArea jt = new JTextArea (7, 50);
        frame.getContentPane ().add (panel);
        jt.setLineWrap (true);
        jt.setWrapStyleWord (true);
        JScrollPane sbtText;
        sbtText = new JScrollPane (jt);
        sbtText.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        panel.add (sbtText);
        // Send Button
        JButton btnSend; // Send Message
        btnSend = new JButton ("SEND");
        btnSend.addActionListener (
                new ActionListener ()
        {
            public void actionPerformed (ActionEvent e)
            {
                String s = jt.getText ();
                if (!s.equals (""))
                {
                    jt.selectAll ();
                    // Send the string
                    sendString (s);
                }
            }
        }
        );
        panel.add (btnSend);
        // Jframe toolbar (text input tools)
        JToolBar toolbar = new JToolBar ("Toolbar", JToolBar.HORIZONTAL);
        frame.getContentPane ().add (toolbar, BorderLayout.NORTH);
        // BOLD BUTTON
        JButton boldbutton = new JButton (new ImageIcon ("text-bold-icon.png"));
        toolbar.add (boldbutton);
        boldbutton.addActionListener (
                new ActionListener ()
        {
            public void actionPerformed (ActionEvent e)
            {
                //setSelectionColor(Color.green);
            }
        }
        );
        // ITALIC BUTTON
        JButton italicbutton = new JButton (new ImageIcon ("text-italic-icon.png"));
        toolbar.add (italicbutton);
        // UNDERLINE BUTTON
        JButton underlinebutton = new JButton (new ImageIcon ("text-underline-icon.png"));
        toolbar.add (underlinebutton);
        // FONT BUTTON
        JButton fontbutton = new JButton (new ImageIcon ("font-icon.png"));
        toolbar.add (fontbutton);
        // COLOR BUTTON
        JButton colorbutton = new JButton (new ImageIcon ("color-wheel-icon.png"));
        toolbar.add (colorbutton);
        // LINK BUTTON
        JButton linkbutton = new JButton (new ImageIcon ("page-white-link-icon.png"));
        toolbar.add (linkbutton);
        // UPLOAD BUTTON
        JButton uploadbutton = new JButton (new ImageIcon ("page-white-get-icon.png"));
        toolbar.add (uploadbutton);
        // SAVE BUTTON
        JButton savebutton = new JButton (new ImageIcon ("disk-icon.png"));
        toolbar.add (savebutton);
        // Jframe settings
        frame.setUndecorated (true);
        frame.getRootPane ().setWindowDecorationStyle (JRootPane.PLAIN_DIALOG);
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.setSize (600, 500);
        frame.setVisible (true);

    } // main method


    private static void sendString (String s)
    {
        Writer w;
        w = new Writer (ta);
        w.Writer (jt, session);
    }


    public static boolean login (String Login) throws IOException
    {
        String lineDump; //to read file
        BufferedReader input = new BufferedReader (new FileReader ("login.txt"));
        int i = 0;
        boolean passcheck = false; //to return
        while (i == 0)
        {
            lineDump = input.readLine ();
            if (lineDump == null)
            {
                passcheck = false;
                break;
            }
            if (Login.equals (lineDump))
            {
                passcheck = true;
                break;
            }
        }
        return passcheck;
    }
} // Irc class

It's exactly what the message says. An inner class cannot use the variables declared in the enclosing method unless they are declared final.
If you don't need to change the value of jt just add the "final" keyword to its declaration.
(It's because variables declared in the method go out of scope and vanish when the method finishes, but the inner class instance will be around long after that. Provided the var is final (ie a constant) the inner class can take its own secret copy of its value to use later; but only if its a constant, ie final)

I understand that, but where do you declare the variable as final?

final JTextArea ta = new JTextArea (text, 15, 50);
        ta.setLineWrap (true);
        JScrollPane sbrText;
        sbrText = new JScrollPane (ta);
        sbrText.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        panel.add (sbrText);
        // Input text area
        final JTextArea jt = new JTextArea (7, 50);

Nevermind, we found out where to place the final declaration.

However, we are running into another error:

The method "void Write(java.lang.String $1, java.lang.String $2) throws java.io.IOException;" can throw the checked exception "java.io.IOException", so its invocation must be enclosed in a try statement that catches the exception, or else this method must be declared to throw the exception.

Here is the code: (We are no longer using the sendString method)

// Send Button
        JButton btnSend; // Send Message
        btnSend = new JButton ("SEND");
        btnSend.addActionListener (
                new ActionListener ()
        {
            public void actionPerformed (ActionEvent e) 
            {
                String s = jt.getText ();
                if (!s.equals (""))
                {
                try {
                    jt.selectAll ();
                    Writer w;
                    w = new Writer (ta);
                    w.Write (s, session);
                    }
                    finally{}
                }
            }
        }
        );
        panel.add (btnSend);

the keyword "final" goes anywhere before the type in the variable declaration, as in
final int i;
static final String s1;
final static String s2;
public final static ArrayList<Boolean> bb;
etc
It just tells the compiler that the variable will be initialised either where it is declared or in the class contructor, and will never change its value thereafter.

Found the fix again: added: catch(Exception ex){}

// Send Button
        JButton btnSend; // Send Message
        btnSend = new JButton ("SEND");
        btnSend.addActionListener (
                new ActionListener ()
        {
            public void actionPerformed (ActionEvent e) 
            {
                String s = jt.getText ();
                if (!s.equals (""))
                {
                try {
                    jt.selectAll ();
                    Writer w;
                    w = new Writer (ta);
                    w.Write (s, session);
                    }
                    catch(Exception ex){}
                }
            }
        }
        );
        panel.add (btnSend);

(oh well)

Once again - just like it says -

invocation must be enclosed in a try statement that catches the exception, or else this method must be declared to throw the exception.

as in

try {
   ...
   w.Write (... // should be lower case first letter for method names
   ...
} catch(IOException e) {
   e.printStackTrace(); // get all the details of what went wrong
}

That's twice you found the problem while I was answering it! Maybe spend a little longer looking before posting???
:-)
J

But NEVER NEVER NEVER have an empty catch clause - things will go wrong, Java creates a detailed error message, you catch it and ignore it, so you never get to find out what happened. (sSee my last post)

Edited 5 Years Ago by JamesCherrill: NEVER empty catch

This article has been dead for over six months. Start a new discussion instead.