1

Suppose I am creating a custom JTable with CustomDataModel and CustomTableListener. Suppose they work gloriously.

Now suppose in the implementation that every JTable is guaranteed to have its first row populated with type-safe data, and that that data will never get deleted, only modified - and never set to null. (using JComboBoxes as their editor, and empty String's and 0's that get rendered as empty strings are the only "empty" choice)

Now; I want to write a method that uses getColumnClass to return typed-data.

From what I've read I'll have to use the following methods in conjunction:

class CustomDataModel extends AbstractTableModel {
...
//implement data struc and other methods (full testable code further down below in the SSCCE)
...

/**
 * Guaranteed to return a class based on this table's construction
 * @param c
 * @return 
 */
@Override
public Class getColumnClass(int c){
    return getValueAt(0,c).getClass();
}

...

public <T> T returnType(int row, int column) {
    //the following will not compile - and I'm stuck, don't know how to
    //use getColumnClass to return type-specific data
    return getColumnClass(column).cast(getValueAt(row,column));
}
}

NetBeans tells me that the cast invocation returns Object, but I was sure that cast(Object obj) returned T, where T is the type of this in cast.

The more I think about it, the more that I believe that I what I desire is impossible. It's not really necessary, but it would avoid casts - although I suppose this would force casts to be done if my current implementation gets "fixed" and typed-retrieval done through manual casting.

Now; in the SSCCE I use system.out.println to just print - which takes in an Object reference and invokes its toString() method, but the methods or actions I want to take I don't necessarily want to be bound to an Object parameter.

The point is to directly get the type of the data stored; which isn't really possible if it's stored as an Object reference without casting it back to its original type I suppose... Unless generics are used? I don't know - any help appreciated!

SSCCE

//package utility;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

/**
 * @author Sean
 */
public class UnitTesting extends JPanel{

    public UnitTesting() {
        super(new GridLayout(1,0));

        JTable table = createAJTable();
        table.getTableHeader().setReorderingAllowed(false);
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);

        //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Normally here JComboBoxes are set as the Editors - SSCCE will use dummy test data

        //Add the scroll pane to this panel.
        add(scrollPane);

        //Test the data Types - I want to retrieve type-specific data to avoid casting
        CustomModel dataModel = (CustomModel)table.getModel();
        Object val;
        for(int row = 0; row < dataModel.getRowCount(); row++){
            //Row possibility
            doSomeThings(row, dataModel);
            for(int col = 0; col < dataModel.getColumnCount(); col++){
                //Here's the old way of going about this. (also could invoke getColumnClass())
                val = dataModel.getValueAt(row, col);
                System.out.println(val + " : " + val.getClass());
                //Overloaded possibility - needs typed data
//                doSomeAction(dataModel.typedValueAt(row, col));
            }
        }
    }

    private JTable createAJTable() {
        return new JTable(new CustomModel(new String[]{"Col1", "Col2", "Col3", "Col4", "Col5"}));
    }

    private void doSomeAction(String str){
        //Do things with strings
    }

    private void doSomeAction(int number){
        //Do things with integers
    }

    private void doSomeThings(int row, CustomModel dataModel) {
        String col1Data, col2Data, col5Data;
        int col3Num, col4Num;
        //Retrieve data and store it in typed local variable (need casting or typed retrieval)
        //Do things with the Strings and Integers together
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("TableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        UnitTesting newContentPane = new UnitTesting();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        /*
         * Set Look and feel here; taken out for SSCCE
         */
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }

    class CustomModel extends AbstractTableModel {
        //Dummy data for the SSCCE
        private List<String> columnHeadings;
        private List<List<Object>> data;

        CustomModel(String[] cols){
            data = new ArrayList<>();
            columnHeadings = Arrays.asList(cols);

            ArrayList<Object> testRow = new ArrayList<>();
            testRow.add("String");
            testRow.add("Str");
            testRow.add(0);
            testRow.add(5);
            testRow.add("Strong");

            data.add(testRow);
        }

        //This is the method I want to create
//        public <T> T typedValueAt(int row, int column) {
//            return getColumnClass(column).cast(getValueAt(row,column));
//        }

        @Override
        public int getRowCount() {
            return data.size();
        }

        @Override
        public int getColumnCount() {
            return columnHeadings.size();
        }

        @Override
        public String getColumnName(int columnIndex) {
            return columnHeadings.get(columnIndex);
        }

        @Override
        public Class<?> getColumnClass(int c) {
            return getValueAt(0,c).getClass();
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            //For the SSCCE we'll just test with one row of uneditable data
            return false;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return data.get(rowIndex).get(columnIndex);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            //Normally check for row existence and populate necessary rows - but for SSCCE just this will suffice
            data.get(rowIndex).set(columnIndex, aValue);
        }
    }
}
mKorbel
  • 108,320
  • 17
  • 126
  • 296
Sean Newell
  • 932
  • 9
  • 22
  • Looking at the following links - going down the generic data struc trail. http://stackoverflow.com/questions/13229979/java-nested-generic-type-mismatch http://stackoverflow.com/questions/8058470/nested-type-parameters-in-java?rq=1 – Sean Newell Nov 20 '13 at 23:10

1 Answers1

1

First, getColumnClass(int) should return a Class with generic type information or else the static return type of this function will effectively be Class<Object> and hence its cast(Object) function will have return type Object. However, that will not fix your problem. The root of the issue is that getValueAt(int,int) of AbstractTableModel carries no static type information other than Object (i.e. its return type is Object).

When you think about it, getValueAt(int,int) cannot return any static type other than Object since it applies to every column in the table and each could have a different type. In fact, you could add columns at runtime which aren't even thought of at compile time. How could you determine statically what type those columns have?

You will most likely have to resort to using the instanceof operator and runtime casts or re-implementing substantial portions of the JTable framework to add static type information for each column (and most likely lose the ability to add columns at runtime).

Tim
  • 413
  • 5
  • 9