问题格式化JTable中的字段 – Integer和Double之间的差异

更新

当columnClass为Double时,确认为JTable上的错误无法将给定的对象格式化为数字(错误ID:7051636) 。 您可以随意投票,或者如果您有替代(更好)的解决方案,请将其作为对报告的评论发布。


我正在构建一个JTable,其中包含一个扩展AbstractTableModel的自定义表模型。 我的模型需要支持空行显示和排序。 所以我接受了这篇文章来实现它,现在工作得非常好。

我仍然遇到JTable格式化字段的问题。 假设我有以下型号:

public class MyModel extends AbstractTableModel{ public Object[] types= {new Integer(0), ""}; public static final Object EMPTY_ROW = ""; @Override public Object getValueAt(int rowIndex, int columnIndex) { return this.EMPTY_ROW; } public Class getColumnClass(int c) { if (c > this.types.length - 1) return null; else return this.types[c].getClass(); } } 

一切正常。 但如果我有一个Double而不是整数:

 public class MyModel extends AbstractTableModel{ public Object[] types= {new Double(0.0), ""}; ....... 

我会得到一个Illegal Argumentexception:

编辑:@Aaron Digulla建议之后的新堆栈跟踪输出

线程“AWT-EventQueue-0”中的exceptionjava.lang.IllegalArgumentException:无法在java.text.Format.format(Format.java)中将给定的Object格式化为java.text.DecimalFormat.format(DecimalFormat.java:487)中的数字:140)at javax.swing.JTable $ DoubleRenderer.setValue(JTable.java:5352)at javax.swing.table.DefaultTableCellRenderer.getTableCellRendererComponent(DefaultTableCellRenderer.java:237)at javax.swing.JTable.prepareRenderer(JTable.java: 5720)at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2072)at javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:1974)at javax.swing.plaf.basic.BasicTableUI .paint(BasicTableUI.java:1770)javax.swing.plaf.ComponentUI.update(ComponentUI.java:143)at javax.swing.JComponent.paintComponent(JComponent.java:752)at javax.swing.JComponent.paint( JComponent.java:1029)位于javax.swing.Jitcport.paint(JComponent.java:1038)的javax.swing.Jaint中的javax.swing.JComponent.paintChildren(JComponent.java:862)javax.swing.JViewport.paint(JViewport.pava) :747)在javax.swing.JComponent.paintChildren(JComponent.java:862)javax.swing.JComponent.paint(JComponent.java:1038)javax.swing.JComponent.paintChildren(JComponent.java:862)javax .swing.JComponent.paint(JComponent.java:1038)位于javax.swing.JLay上的javax.swing.JComponent.paint(JComponent.java:1038)的javax.swing.JComponent.paintChildren(JComponent.java:862)。在Javax.swing.Baint.Taint.Tran.Paint的Javax.swing.JComponent.paint上面的javax.swing.JComponent.paintChildren(JComponent.java:862)javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java)中绘制(JLayeredPane.java:567) :278)at javax.swing.RepaintManager.paint(RepaintManager.java:1224)at javax.swing.JComponent.paint(JComponent.java:1015)at java.awt.GraphicsCallback $ PaintCallback.run(GraphicsCallback.java:21) at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)at java.awt.Container.paint(Container.java) :1780)at java.awt.Window.paint(Window.java:3375)at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)at javax .swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)位于java的java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)的javax.swing.SystemEventQueueUtilities $ ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)。位于java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)java.awt.EventDispatchThread.pumpEventsForHierarchy的java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)上的awt.EventQueue.dispatchEvent(EventQueue.java:597) (EventDispatchThread.java:174)java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)at java.awt.EventDispatchThread.run(EventDispatchThread.java: 122)

为什么这个?

getValueAt始终返回相同的值以用它填充所有表条目。 这仅适用于调试:

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

例如,如果我改为:

  @Override public Object getValueAt(int rowIndex, int columnIndex) { return new Integer(3); //or return new Double(3.3); //return new String("foobar"); doesn't work } 

即使表的某些字段是String,一切正常。 它告诉我,因为Integer和Double可以转换为String,这不会导致问题。 无论如何,我想了解为什么像我的EMPTY_ROW这样的通用对象可以被接受为声明的Integer字段的值,而这不适用于Double字段。

EDIT2:

如果我在表模型中删除getClass方法。 有用。 无论如何,我想解决这个问题,而不必删除该方法,即使这将迫使我实现一些自定义渲染方法。

EDIT3:

这是一个SSCCE。 向表中添加新值时会出现一些错误,但与渲染问题无关。

 import java.text.ParseException; import java.util.ArrayList; import java.util.Comparator; import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SortOrder; import javax.swing.RowSorter.SortKey; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableRowSorter; public class TableExample extends JFrame{ public static final Object EMPTY_ROW = ""; public class EmptyRowComparator<COLUMN_TYPE extends Comparable> implements Comparator{//extends RuleBasedCollator{ private TableRowSorter sorter; private int column; public EmptyRowComparator(TableRowSorter sorter, int col) throws ParseException { // super(arg0); this.sorter = sorter; this.column = col; // TODO Auto-generated constructor stub } private int getSortOrder() { SortOrder order = SortOrder.ASCENDING; // List keys = sorter.getSortKeys(); // sorter.getSortKeys(); // for (SortKey sortKey : sorter.getSortKeys()) { if (sortKey.getColumn() == this.column) { order = sortKey.getSortOrder(); break; } } return order == SortOrder.ASCENDING ? 1 : -1; } @Override public int hashCode() { // TODO Auto-generated method stub return 0; } @Override public int compare(Object arg0, Object arg1) { // TODO Auto-generated method stub //System.out.println("Comparing Integer arg0 " + arg0 + " arg1 " + arg1); boolean empty1 = arg0 == EMPTY_ROW; boolean empty2 = arg1 == EMPTY_ROW; if (empty1 && empty2) { return 0; } else if (empty1) { return 1 * getSortOrder(); } else if (empty2) { return -1 * getSortOrder(); } return ((Comparable) (COLUMN_TYPE)arg0).compareTo((COLUMN_TYPE)arg1); // return 0; } } public class ConcreteTable extends AbstractTableMod{ // private static final long serialVersionUID = 4672561280810649603L; private String[] columnNames = {"ID", "description"}; Class[] types = {Integer.class, String.class}; //Object[] types = {Double.class, String.class}; private int minimumDisplayedRow; public ConcreteTable(){ //System.out.println("DEBUG ARRAY length " + data.length); this.minimumDisplayedRow = 10; this.datas = new ArrayList<ArrayList>(); for (int i = 0 ; i < this.minimumDisplayedRow ; i++){ this.addEmptyRow(); } for (int i = 0 ; i < 5 ; i++){ ArrayList row = new ArrayList(); row.add(new Integer(i)); row.add(new String("prova " + i)); this.addRow(row); } } public String getColumnName(int col) { System.out.println("getColumnName " + col + " = " + columnNames[col]); return columnNames[col]; } @Override protected Class[] getTypeArray() { // TODO Auto-generated method stub return this.types; } @Override protected ArrayList getKeysColumnIndex() { // TODO Auto-generated method stub ArrayList keys = new ArrayList(); keys.add(0); return keys; } public boolean isCellEditable(int row, int col) { System.out.println("isCellEditable row " + row + " col " + col); if (col == 1){ System.out.println("TRUE"); return true; } return false; } /*note: generated keys must be in the same order they appear in the table*/ @Override protected Object getGeneratedKeys(int col) { // TODO Auto-generated method stub if (col != 0 ) return null; return new Integer(this.rowNumber); } @Override protected int getMinimumDisplayedRow() { // TODO Auto-generated method stub return this.minimumDisplayedRow; } } public abstract class AbstractTableMod extends AbstractTableModel { ArrayList<ArrayList> datas ; protected int rowNumber = 0; protected abstract Class[] getTypeArray(); protected abstract ArrayList getKeysColumnIndex(); protected abstract Object getGeneratedKeys(int col); protected abstract int getMinimumDisplayedRow(); public int getRowCount(){ return this.datas.size() ; } @Override public int getColumnCount() { return this.getTypeArray().length; } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex >= this.rowNumber ){ return EMPTY_ROW; } try{ ArrayList row = this.datas.get(rowIndex); if (row == null) return null; Object obj = row.get(columnIndex); return obj; }catch(IndexOutOfBoundsException e){ return null; } } public void setValueAt(Object value, int row, int col) { //System.out.println("setValueAt object : " + value.getClass().getName()); Class targetColClass = this.getColumnClass(col); if (!targetColClass.isInstance(value)) return; if (value instanceof String){ String stringVal = (String)value; if (stringVal.compareTo("") == 0) return; } if (row >= this.rowNumber){ ArrayList newRow = new ArrayList(); ArrayList keysIndexList = this.getKeysColumnIndex(); for (int i = 0 ; i < this.getColumnCount(); i++){ if (i == col){ newRow.add(value); }else if (keysIndexList.contains(i)){ newRow.add(this.getGeneratedKeys(i)); }else{ newRow.add(EMPTY_ROW); } } this.addRow(newRow); }else{ this.datas.get(row).set(col, value); } this.fireTableCellUpdated(row, col); } public Class getColumnClass(int c) { System.out.println("AbstractTable: getColumnClass"); if (c > this.getTypeArray().length - 1) return null; else return this.getTypeArray()[c]; } public void addEmptyRow(){ ArrayList emptyRow = new ArrayList(); for (int i = 0 ; i < this.getTypeArray().length; i++){ emptyRow.add(EMPTY_ROW); } this.datas.add(emptyRow); } public void addRow(ArrayList row){ Object[] types = this.getTypeArray(); if (types.length != row.size()) return; for (int i = 0 ; i < row.size() ; i++){ Class targetColClass = this.getColumnClass(i); Object rowItem = row.get(i); } this.datas.add(this.rowNumber, row); this.rowNumber++; if (this.rowNumber < this.getMinimumDisplayedRow()) this.datas.remove(this.datas.size() -1 ); this.fireTableRowsInserted(this.rowNumber , this.rowNumber ); } } public TableExample(){ super("JTable example"); getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); ConcreteTable model = new ConcreteTable(); JTable tab = new JTable(model); TableRowSorter sorter = new TableRowSorter(model); try { sorter.setComparator(0, new EmptyRowComparator(sorter,0)); sorter.setComparator(1, new EmptyRowComparator(sorter,1)); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } tab.setRowSorter(sorter); JScrollPane table = new JScrollPane(tab); this.getContentPane().add(table); this.setSize(600, 400); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub new TableExample(); } } 

如果你试图改变

 Class[] types = {Integer.class, String.class}; 

用:

 Class[] types = {Double.class, String.class}; 

你可以看到问题。

Walter Laan是如何在他的post中说的

 Never give up! Never surrender! 

编辑:我无法抗拒,但由于我的英语不好,我不敢评论为什么,在哪里以及如何可能,也没有正确的确认,我为TableColumnRendering添加了Rob的两个(一点点)修改类…

 import java.awt.EventQueue; import java.math.RoundingMode; import java.text.*; import java.util.*; import javax.swing.*; import javax.swing.RowSorter.SortKey; import javax.swing.SwingConstants; import javax.swing.table.*; public class TableExample extends JFrame { public static final Object EMPTY_ROW = ""; private static final long serialVersionUID = 1L; private JTable tab; private Calendar cal; private Date dateWithOutTime = new java.util.Date(); private SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");//standard continental EU date format public class EmptyRowComparator> implements Comparator {//extends RuleBasedCollator{ private TableRowSorter sorter; private int column; public EmptyRowComparator(TableRowSorter sorter, int col) throws ParseException { this.sorter = sorter; this.column = col; } private int getSortOrder() { SortOrder order = SortOrder.ASCENDING; for (SortKey sortKey : sorter.getSortKeys()) { if (sortKey.getColumn() == this.column) { order = sortKey.getSortOrder(); break; } } return order == SortOrder.ASCENDING ? 1 : -1; } @Override public int hashCode() { return 0; } @Override @SuppressWarnings("unchecked") public int compare(Object arg0, Object arg1) { boolean empty1 = arg0 == EMPTY_ROW; boolean empty2 = arg1 == EMPTY_ROW; if (empty1 && empty2) { return 0; } else if (empty1) { return 1 * getSortOrder(); } else if (empty2) { return -1 * getSortOrder(); } return ((Comparable) (COLUMN_TYPE) arg0).compareTo((COLUMN_TYPE) arg1); } } public class ConcreteTable extends AbstractTableMod { private static final long serialVersionUID = 4672561280810649603L; private String[] columnNames = {"Integer", "String", "Integer", "Double", "Boolean", "Double", "String", "Boolean", "Date"}; private Class[] types = {Integer.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class}; private int minimumDisplayedRow; public ConcreteTable() { this.minimumDisplayedRow = 10; this.datas = new ArrayList>(); for (int i = 0; i < this.minimumDisplayedRow; i++) { this.addEmptyRow(); } Random rnd = new Random(); for (int i = 0; i < 7; i++) { ArrayList row = new ArrayList(); row.add(i); row.add(((rnd.nextInt(25)) + "prova")); row.add(rnd.nextInt(25)); row.add(rnd.nextInt(25) + 3.14); row.add((i % 2 == 0) ? true : false); row.add(rnd.nextInt(25) + 3.14); row.add(((rnd.nextInt(25)) + "prova")); row.add((i % 2 == 0) ? false : true); cal = Calendar.getInstance(); cal.add(Calendar.DATE, -rnd.nextInt(25)); dateWithOutTime = cal.getTime(); String nullTimeForDateString = sdf.format(dateWithOutTime); try { dateWithOutTime = sdf.parse(nullTimeForDateString); } catch (ParseException ex) { } row.add(dateWithOutTime); this.addRow(row); } } @Override public String getColumnName(int col) { System.out.println("getColumnName " + col + " = " + columnNames[col]); return columnNames[col]; } @Override protected Class[] getTypeArray() { return this.types; } @Override protected ArrayList getKeysColumnIndex() { ArrayList keys = new ArrayList(); keys.add(0); return keys; } @Override public boolean isCellEditable(int row, int col) { System.out.println("isCellEditable row " + row + " col " + col); if (col == 1) { System.out.println("TRUE"); return true; } return false; } @Override protected Object getGeneratedKeys(int col) { if (col != 0) { return null; } return new Integer(this.rowNumber); } @Override protected int getMinimumDisplayedRow() { return this.minimumDisplayedRow; } } public abstract class AbstractTableMod extends AbstractTableModel { private static final long serialVersionUID = 1L; protected ArrayList> datas; protected int rowNumber = 0; protected abstract Class[] getTypeArray(); protected abstract ArrayList getKeysColumnIndex(); protected abstract Object getGeneratedKeys(int col); protected abstract int getMinimumDisplayedRow(); public int getRowCount() { return this.datas.size(); } @Override public int getColumnCount() { return this.getTypeArray().length; } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex >= this.rowNumber) { return EMPTY_ROW; } try { ArrayList row = this.datas.get(rowIndex); if (row == null) { return null; } Object obj = row.get(columnIndex); return obj; } catch (IndexOutOfBoundsException e) { return null; } } @Override public void setValueAt(Object value, int row, int col) { Class targetColClass = this.getColumnClass(col); if (!targetColClass.isInstance(value)) { return; } if (value instanceof String) { String stringVal = (String) value; if (stringVal.compareTo("") == 0) { return; } } if (row >= this.rowNumber) { ArrayList newRow = new ArrayList(); ArrayList keysIndexList = this.getKeysColumnIndex(); for (int i = 0; i < this.getColumnCount(); i++) { if (i == col) { newRow.add(value); } else if (keysIndexList.contains(i)) { newRow.add(this.getGeneratedKeys(i)); } else { newRow.add(EMPTY_ROW); } } this.addRow(newRow); } else { this.datas.get(row).set(col, value); } this.fireTableCellUpdated(row, col); } @Override @SuppressWarnings("unchecked") public Class getColumnClass(int c) { if (c > this.getTypeArray().length - 1) { return null; } else { return this.getTypeArray()[c]; } } public void addEmptyRow() { ArrayList emptyRow = new ArrayList(); for (int i = 0; i < this.getTypeArray().length; i++) { emptyRow.add(EMPTY_ROW); } this.datas.add(emptyRow); } public void addRow(ArrayList row) { Object[] types = this.getTypeArray(); if (types.length != row.size()) { return; } for (int i = 0; i < row.size(); i++) { Class targetColClass = this.getColumnClass(i); Object rowItem = row.get(i); } this.datas.add(this.rowNumber, row); this.rowNumber++; if (this.rowNumber < this.getMinimumDisplayedRow()) { this.datas.remove(this.datas.size() - 1); } this.fireTableRowsInserted(this.rowNumber, this.rowNumber); } } public TableExample() { super("JTable example"); getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); ConcreteTable model = new ConcreteTable(); tab = new JTable(model); TableRowSorter sorter = new TableRowSorter(model); try { sorter.setComparator(0, new EmptyRowComparator(sorter, 0)); sorter.setComparator(1, new EmptyRowComparator(sorter, 1)); sorter.setComparator(2, new EmptyRowComparator(sorter, 2)); sorter.setComparator(3, new EmptyRowComparator(sorter, 3)); sorter.setComparator(4, new EmptyRowComparator(sorter, 4)); sorter.setComparator(5, new EmptyRowComparator(sorter, 5)); sorter.setComparator(6, new EmptyRowComparator(sorter, 6)); sorter.setComparator(7, new EmptyRowComparator(sorter, 7)); sorter.setComparator(8, new EmptyRowComparator(sorter, 8)); } catch (ParseException e) { e.printStackTrace(); } tab.setRowSorter(sorter); JScrollPane table = new JScrollPane(tab); this.getContentPane().add(table); this.setSize(800, 400); setDefaultCloseOperation(EXIT_ON_CLOSE); setRenderers(); EventQueue.invokeLater(new Runnable() { @Override public void run() { setVisible(true); } }); //TableExample tableExample = new TableExample(); } public void setRenderers() { TableColumnModel m = tab.getColumnModel(); //"Integer", "String", "Interger", "Double", "Boolean", "Double", "String", "Boolean", "Date" m.getColumn(0).setCellRenderer(NumberRenderer.getIntegerRenderer()); m.getColumn(2).setCellRenderer(NumberRenderer.getIntegerRenderer()); m.getColumn(3).setCellRenderer(NumberRenderer.getDoubleRenderer5()); m.getColumn(5).setCellRenderer(NumberRenderer.getDoubleRenderer3()); m.getColumn(8).setCellRenderer(FormatRenderer.getDateRenderer()); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { TableExample tableExample = new TableExample(); } }); TableExample tableExample = new TableExample(); } } class FormatRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; private Format formatter; private static DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");//standard continental EU date format FormatRenderer(Format formatter) { this.formatter = formatter; } @Override public void setValue(Object value) { try { if ((value != null)) { if ((value instanceof Number) || (value instanceof Date)) { setHorizontalAlignment(SwingConstants.RIGHT); value = formatter.format(value); } } } catch (IllegalArgumentException e) { } super.setValue(value); } public static FormatRenderer getDateRenderer() { return new FormatRenderer(dateFormat); } } class NumberRenderer extends FormatRenderer { private static final long serialVersionUID = 1L; private static Number numberValue; private static NumberFormat nf; NumberRenderer(NumberFormat formatter) { super(formatter); setHorizontalAlignment(SwingConstants.RIGHT); } public static NumberRenderer getIntegerRenderer() { return new NumberRenderer(NumberFormat.getIntegerInstance()); } public static NumberRenderer getDoubleRenderer3() { nf = NumberFormat.getNumberInstance(); nf.setMinimumFractionDigits(3); nf.setMaximumFractionDigits(3); nf.setRoundingMode(RoundingMode.HALF_UP); return new NumberRenderer(nf); } public static NumberRenderer getDoubleRenderer5() { nf = NumberFormat.getNumberInstance(); nf.setMinimumFractionDigits(5); nf.setMaximumFractionDigits(5); nf.setRoundingMode(RoundingMode.HALF_UP); return new NumberRenderer(nf); } } 

reall问题与以前的辩论有关,有普通的香草(比较我缺少的英语技能),你认为那真的是Bug,或者如果我展示了ViceVersaView,也许那么somone能够为JTable + TableModel评论那些链用英语写的+ Comparator(TableRowSorter for JTable API),eeeeeerghhh在这种情况下真的很糟糕

 import java.awt.*; import java.awt.event.*; import java.util.Date; import javax.swing.*; import javax.swing.table.*; public class ViceVersaBugFromTableModelAndComparator { private String[] columnNames = {"String", "Integer", "Boolean", "Double", "Date"}; private Object[][] data = { {"aaa", 12, true, .15, new Date()}, {"bbb", 5, false, 100.01, new Date()}, {"CCC", 92, true, 15.2, new Date()}, {"DDD", 0, false, 10.80, new Date()}, {"abc", 5, true, 4.11, new Date()}, {"bae", 31, false, 100.01, new Date()}, {"CAX", 27, true, 2.3, new Date()}, {"AXD", 4, false, 50.00, new Date()}, {"abc", 3, true, 1.5, new Date()}, {"bae", 5, false, 1000, new Date()}, //java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String //{"bae", 5, false, 1000.0, new Date()}, //un-comment for correctness {"CAX", 2, true, 21.7, new Date()}, {"AXD", 2, false, 5.30, new Date()} }; private TableModel model = new DefaultTableModel(data, columnNames) { private static final long serialVersionUID = 1L; @Override public Class getColumnClass(int column) { return String.class;// again java.lang.ClassCastException //return getValueAt(0, column).getClass(); //un-comment for correctness } }; private JTable table = new JTable(model); private JTableHeader header; static class TestTableRowSorter extends TableRowSorter { public TestTableRowSorter(TableModel m) { super(m); } @Override public void toggleSortOrder(int column) { } public void wrapToggleSortOrder(int column) { super.toggleSortOrder(column); } } private Timer timer = new Timer(400, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("single"); JTable table = header.getTable(); RowSorter sorter; if (pt != null && table != null && (sorter = table.getRowSorter()) != null) { int columnIndex = header.columnAtPoint(pt); if (columnIndex != -1) { columnIndex = table.convertColumnIndexToModel(columnIndex); ((TestTableRowSorter) sorter).wrapToggleSortOrder(columnIndex); } } } }); private Point pt; public JComponent makeUI() { timer.setRepeats(false); table.setRowSorter(new TestTableRowSorter(model)); header = table.getTableHeader(); header.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(final MouseEvent e) { if (timer.isRunning() && !e.isConsumed() && e.getClickCount() > 1) { System.out.println("double"); pt = null; timer.stop(); } else { pt = e.getPoint(); timer.restart(); } } }); JPanel p = new JPanel(new BorderLayout()); p.add(new JScrollPane(table)); return p; } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } public static void createAndShowGUI() { JFrame f = new JFrame(); f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); f.getContentPane().add(new ViceVersaBugFromTableModelAndComparator().makeUI()); f.setSize(820, 240); f.setLocationRelativeTo(null); f.setVisible(true); } } 

问题不是双重问题,而是其他问题。 正如您在堆栈跟踪中看到的,该表特别支持double值( javax.swing.JTable$DoubleRenderer )。

我们的问题是传递给setValue()值不是Double而是其他东西。

问题是:当你格式化Double值时,它变成一个字符串,你不能将它返回,因为列只允许Double.class。 因此,如果您将Double.class更改为String.class,它将起作用。