RowFilter.NumberFilter:无法处理“混合”具体数字类型

发布于 2024-12-13 11:10:33 字数 1606 浏览 0 评论 0原文

如果至少有一个值(RowFilter 中的值 == 值,条目中的值)是小数,则会发生这种情况。这是一个失败的测试:

@Test
public void testRowFilterNumberMixCore() {
    TestEntry entry = new TestEntry(1.2f);
    RowFilter filter = RowFilter.numberFilter(ComparisonType.AFTER, 1, 0);
    assertTrue(entry + "must be included " + filter, filter.include(entry));
}

输出是:

junit.framework.AssertionFailedError: 
[entry: 1.2] must be included [RowFilter: ComparisonType = AFTER, comparableValue: 1, comparableClass: class java.lang.Integer]

原因是 NumberFilter 会回退到通过 number.longValue() 比较数字,如果它们不是同一类(并且通过相互比较)

知道细节,测试失败并不令人惊讶(事后看来,从来没有想到这是一个问题;-)防御的一个级别是确保 - 在客户端代码中 - 要比较的数字相同班级。这并不总是可能的(想想fi:带有columnClass Number的tableColumn)所以我想知道是否/如何改进后备。像这样的事情:

if (one instanceof Comparable && one.getClass() == other.getClass()) {
    // same class, use comparator
    return ((Comparable) one).compareTo(other);
}
if (areIntegers(one, other)) {
    // all integers, use longValue
    return longCompare(one, other);
}
if (areDecimals(one, other)) {
    // anything to do here?
}
// at last resort convert to BigDecimal and compare those: 
BigDecimal bigOne = new BigDecimal(one.toString());
BigDecimal bigOther = new BigDecimal(other.toString());
return bigOne.compareTo(bigOther);

这样做可以使测试通过 - 我对隐藏的(阅读:我不知道:)陷阱有点警惕。非常欢迎任何警告/替代方案!

仅供参考:交叉发布到 OTN 的 Swing 论坛

按照上面概述的方式实施后续行动

,现在等待客户投诉 - 在这种情况下,我们将指责所有没有在这里警告我的人:-)

Happens if at least one of the values (values == value in RowFilter, value in entry) is a decimal. Here's a failing test:

@Test
public void testRowFilterNumberMixCore() {
    TestEntry entry = new TestEntry(1.2f);
    RowFilter filter = RowFilter.numberFilter(ComparisonType.AFTER, 1, 0);
    assertTrue(entry + "must be included " + filter, filter.include(entry));
}

The output is:

junit.framework.AssertionFailedError: 
[entry: 1.2] must be included [RowFilter: ComparisonType = AFTER, comparableValue: 1, comparableClass: class java.lang.Integer]

The reason is that NumberFilter falls back to comparing the numbers by their number.longValue() if they are not the same class (and by that comparable to each other)

Knowing that detail, the test failure is not astonishing (in hind-sight, would have never thought of that being an issue ;-) One level of defense is to make sure - in client code - that the numbers to compare are of the same class. That's not always possible (think f.i.: a tableColumn with columnClass Number) So I'm wondering if/how to improve on the fallback. Something like:

if (one instanceof Comparable && one.getClass() == other.getClass()) {
    // same class, use comparator
    return ((Comparable) one).compareTo(other);
}
if (areIntegers(one, other)) {
    // all integers, use longValue
    return longCompare(one, other);
}
if (areDecimals(one, other)) {
    // anything to do here?
}
// at last resort convert to BigDecimal and compare those: 
BigDecimal bigOne = new BigDecimal(one.toString());
BigDecimal bigOther = new BigDecimal(other.toString());
return bigOne.compareTo(bigOther);

Doing so, makes the test pass - I'm a bit wary about hidden (read: unknown to me :) pitfalls. Any warnings/alternatives highly welcome!

FYI: cross-posted to OTN's Swing forum

Follow-up

implemented as outlined above, now waiting for clients to complain - in that case will point fingers to everybody who didn't warn me here :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

画骨成沙 2024-12-20 11:10:33

我没有更好的答案,但下面的例子说明了效果。特别是,基于 double 原语的 RowFilter装箱Double,生成具有values > 的预期tableau 1..相比之下,基于 float 的则被装箱为 Float。由于类文字不匹配,include() 会比较 long 值,意外地过滤了所有小数 2..

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.RowFilter;
import javax.swing.RowFilter.ComparisonType;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;

/** @see http://stackoverflow.com/questions/7993546 */
public class FilterTest {

    private static TableRowSorter<TableModel> sorter;
    private static RowFilter<TableModel, Integer> dFilter;
    private static RowFilter<TableModel, Integer> fFilter;
    private static boolean b;

    public static void main(String[] args) {
        TableModel model = new TableModel();
        JTable table = new JTable(model);
        sorter = new TableRowSorter<TableModel>(model);
        dFilter = RowFilter.numberFilter(ComparisonType.AFTER, 1d, 0);
        fFilter = RowFilter.numberFilter(ComparisonType.AFTER, 1f, 0);
        sorter.setRowFilter(dFilter);
        table.setRowSorter(sorter);
        JScrollPane scrollPane = new JScrollPane(table);
        table.setPreferredScrollableViewportSize(new Dimension(320, 240));

        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(scrollPane, BorderLayout.CENTER);
        f.add(new JToggleButton(new AbstractAction("Toggle") {

            @Override
            public void actionPerformed(ActionEvent e) {
                b = !b;
                if (b) {
                    sorter.setRowFilter(fFilter);
                } else {
                    sorter.setRowFilter(dFilter);
                }
            }
        }), BorderLayout.SOUTH);

        f.pack();
        f.setVisible(true);
    }

    private static class TableModel extends AbstractTableModel {

        private static final int ROWS = 16;
        private static final int COLS = 4;
        private Double[][] matrix = new Double[ROWS][COLS];

        public TableModel() {
            double v = 0;
            for (Object[] row : matrix) {
                Arrays.fill(row, Double.valueOf(v += 0.25));
            }
        }

        @Override
        public int getRowCount() {
            return ROWS;
        }

        @Override
        public int getColumnCount() {
            return COLS;
        }

        @Override
        public Object getValueAt(int row, int col) {
            return matrix[row][col];
        }

        @Override
        public Class<?> getColumnClass(int col) {
            return Number.class;
        }
    }
}

I don't have a better answer, but the example below illustrates the effect. In particular, a RowFilter based on a double primitive is boxed as Double, producing the expected tableau having values > 1. In contrast, the one based on a float is boxed as Float. Because the class literals do not match, include() compares the long values, unexpectedly filtering all fractional values < 2.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.RowFilter;
import javax.swing.RowFilter.ComparisonType;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;

/** @see http://stackoverflow.com/questions/7993546 */
public class FilterTest {

    private static TableRowSorter<TableModel> sorter;
    private static RowFilter<TableModel, Integer> dFilter;
    private static RowFilter<TableModel, Integer> fFilter;
    private static boolean b;

    public static void main(String[] args) {
        TableModel model = new TableModel();
        JTable table = new JTable(model);
        sorter = new TableRowSorter<TableModel>(model);
        dFilter = RowFilter.numberFilter(ComparisonType.AFTER, 1d, 0);
        fFilter = RowFilter.numberFilter(ComparisonType.AFTER, 1f, 0);
        sorter.setRowFilter(dFilter);
        table.setRowSorter(sorter);
        JScrollPane scrollPane = new JScrollPane(table);
        table.setPreferredScrollableViewportSize(new Dimension(320, 240));

        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(scrollPane, BorderLayout.CENTER);
        f.add(new JToggleButton(new AbstractAction("Toggle") {

            @Override
            public void actionPerformed(ActionEvent e) {
                b = !b;
                if (b) {
                    sorter.setRowFilter(fFilter);
                } else {
                    sorter.setRowFilter(dFilter);
                }
            }
        }), BorderLayout.SOUTH);

        f.pack();
        f.setVisible(true);
    }

    private static class TableModel extends AbstractTableModel {

        private static final int ROWS = 16;
        private static final int COLS = 4;
        private Double[][] matrix = new Double[ROWS][COLS];

        public TableModel() {
            double v = 0;
            for (Object[] row : matrix) {
                Arrays.fill(row, Double.valueOf(v += 0.25));
            }
        }

        @Override
        public int getRowCount() {
            return ROWS;
        }

        @Override
        public int getColumnCount() {
            return COLS;
        }

        @Override
        public Object getValueAt(int row, int col) {
            return matrix[row][col];
        }

        @Override
        public Class<?> getColumnClass(int col) {
            return Number.class;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文