Search code examples
javaswingjtableprivatetablemodel

I'm not sure why a variable is inaccessible


I'm writing a small program that creates a gui to display the contents of a csv file. I've tried following the outline from the Oracle website (http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#data), but my problem is that the 'getColumnCount' method that is used to build the table can't access the 'headers' variable. Or more likely, it can, but the changes I thought I made to it in the main method did not connect to it. If anyone can shed some light on what's wrong and how to fix it, it'd be much appreciated.

public class MyTableModel implements TableModel {

    private String[] headers;        //This line.
    private Object[][] tableData;

    public static void main(String[] args) {
        String fileName = "products.csv";

        String[] csvList = readCSV(fileName);

        String[] headers = Arrays.copyOfRange(csvList, 0, 10); //Or maybe this line isn't changing the one above.
    }

    private static String[] readCSV(String file) {
        //Some code to fill the list.
        return fileString;
    }

    @Override
    public int getColumnCount() {
        return headers.length;        //<<This line of code
    }
}

@Hovercraft Full Of Eels

Oh, I should have mentioned. I'm implementing this class like this, which is to say, I'm calling it from elsewhere.

private static void createGUI() {
    csvTabler table = new csvTabler();
    table.setTitle("CSV Table");
    table.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    table.createJTable();
    table.pack();
    table.setVisible(true);
}

private void createJTable() {
    jTable = new JTable(new MyTableModel());
}

I'm sure this affects your solution but I'm not sure how to adjust..


Solution

  • String[] headers = Arrays.copyOfRange(csvList, 0, 10); //Or maybe this line isn't changing the one above.
    

    Yep, that's it in a nutshell .... you're trying to change an instance field from a static method and are also shadowing the variable to boot, and that just won't work. Understand that the headers variable declared within the main method is local to this method -- visible only within the method -- and so changes to it will have absolutely no effect on the headers instance field in the class. Instead create a constructor and pass the header data in when you need to pass it into the class.

    A bad idea is to make headers static -- just don't do this as this throws out the OOPs baby with the bathwater, essentially fixing your problem with a kludge rather than making a much cleaner more fundamental improvement to your program.

    For example:

    public class MyTableModel implements TableModel {
    
        private String[] headers;        //This line.
        private Object[][] tableData;
    
    
        public MyTableModel(String[] headers, Object[][] tableData) {
            this.headers = headers;
            this.tableData = tableData;
        }
    
        @Override
        public int getColumnCount() {
            return headers.length;        //<<This line of code
        }
    
        public static void main(String[] args) {
            String fileName = "products.csv";
    
            String[] csvList = readCSV(fileName);
    
            String[] headers = Arrays.copyOfRange(csvList, 0, 10); 
            Object[][] tableData = ..MyTableModel.. // code to create this
    
            // now create a table model with your data and use it.
            MyTableModel myTableModel = new MyTableModel(headers, tableData);
        }
    
        private static String[] readCSV(String file) {
            String fileString = "";
            //Some code to fill the list.
            return fileString;
        }
    
    }
    

    Other issues: You should almost never implement TableModel but rather extend either DefaultTableModel or AbstractTableModel. Otherwise your model will miss most of the necessary machinery to make it work.

    Regarding:

    What if I made the instance field static as well? But assuming that no such easy option exists. Do I do away with my main() method? I suspected that a constructor would be better, but the main method was helpful for testing at first, and I was getting a lot of errors with the constructor I tried to build.

    Again, avoid statics as this increases connectedness of your code, its "coupling" without benefit which greatly increases your risk of hard to find bugs as your program grows.

    Regarding, "do I do away with my main method" -- but of course your program will need a main method somewhere, so you already know the answer to this. The main method should be small and should serve only to set the pieces of the application in motion, and nothing more.

    regarding "I suspected that a constructor would be better, but the main method was helpful for testing at first, and I was getting a lot of errors with the constructor I tried to build." -- a constructor is necessary, the main method and the constructor are no mutually exclusive, and as for errors -- fix them, one at a time.