I want to implement a TableModel, that will enable my existing data structure to be displayed by a JTable.
Say my data structure is a simple List<MyRow>, where MyRow is:
public class MyRow {
String firstName;
String secondName;
}
My idea is that this class “should do its part” in being displayed, so i add it the following method, that simply maps each field to an index:
public Object getValueAt(int columnIndex) {
switch (columnIndex) {
case 0: return firstName;
case 1: return secondName;
default: return null;
}
}
At this point, the TableModel implementation will simply be:
public class MyModel extends AbstractTableModel {
ArrayList<MyRow> list = new ArrayList<MyRow>();
public int getRowCount() {return list.size();}
public int getColumnCount() {return 2;}
public Object getValueAt(int rowIndex, int columnIndex) {
return list.get(rowIndex).getValueAt(columnIndex);
}
}
Neat, isn’t it? 🙂
Ok, but return 2; annoys me. In this “architecture” i have assigned to MyRow the rexposibility to represent a row, and so it should be MyRow that gives the number of columns. That because it provides the getValueAt(int columnIndex) method that is related to the number of columns: columnsIndex<columnCount.
This piece of information may be asked even if there are no rows in the List, so no instances of MyRow. So the method to add to MyRow should be a static method:
public class MyRow {
...
public static int getColumnCount() { return 2; }
...
And the getColumnCount() method of MyModel will be simply modified accordingly:
public int getColumnCount() {
//return 2; //No more this *Encapsulation Violation*
return MyRow.getColumnCount();
}
Now MyRow contains all the data needed to represent a row. It is easy to show that: adding, removing or changing columns is just question of modifying MyRow.
Clean, isn’t it? 🙂 🙂
The problem is that with static methods, i can’t provide an abstract super-class for MyRow.
This should be obvious, but to be sure:
public abstract class MyAbstractRow {
public static int getColumnCount() { return 0; }
}
then
public class MyRow extends MyAbstractRow{
String firstName;
String secondName;
public static int getColumnCount() { return 2; }
}
then i implement a “generic” MyData, that uses a MyAbstractRow:
public int getColumnCount() {
//return 2; //No more this *Encapsulation Violation*
return MyAbstractRow.getColumnCount();
}
This is resolved on the static type (so returns 0) because the getColumnCount of MyData doesn’t even have a reference to the type i actually want to use as my concrete implementation of MyAbstractRow.
How to solve this problem, mantaining the good level of encapsulation achieved so far?
You are mixing model and view concepts. Your model MyRow should not know how it will be represented and so it should not know how many columns there are, what they contain, etc… This is why you run into this problem (using a static method to know the number of columns).
One way to do this more cleanly is to have your TableModel return the number of columns and also implement the getValueAt by invoking the members of your MyRow class. Did you take a look at the default implementation (DefaultTableModel).
Now I don’t understand why you need to override static methods (which is impossible). If you need to have different table models for different objects that inherit each other, then you might consider creating different table models that inherit each other in the same way.