You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JFormattedTextField.isEditValid()负数校验返回值异常问题

问题场景

程序中为JTable实现了基于JFormattedTextField的自定义单元格编辑器,业务逻辑依赖JFormattedTextField的isEditValid()方法返回值判断是否执行后续数据处理,该方法返回结果的准确性直接决定功能是否正常。

异常现象

输入相同负数值时,isEditValid()返回结果不稳定:部分环境返回true,部分环境返回false。初期怀疑是Java版本差异导致——Corretto 11版本下返回false,Oracle JDK 1.8版本下返回true。返回false的异常场景中,按下Enter键无法触发编辑结束,必须点击单元格外部区域才会触发生成输出日志。
复现代码如下:

import java.awt.*;
import java.math.RoundingMode;
import java.text.NumberFormat;

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.DefaultFormatterFactory;
import javax.swing.text.NumberFormatter;

class SimpleTest extends JFrame
{
    private JPanel topPanel;
    private JTable table;
    private JScrollPane scrollPane;
    private String[] columnNames=new String[3];
    private String[][] dataValues=new String[3][3];

    public SimpleTest()
    {
        setTitle("JTable Cell Validation");
        setSize(300,300);
        setDefaultCloseOperation (EXIT_ON_CLOSE);
        topPanel=new JPanel();
        topPanel.setLayout(new BorderLayout());
        getContentPane().add(topPanel);
        columnNames=new String[] {"Name" ,"Value"};
        dataValues=new String[][] {  {"DummyField ","0"} };
        
        TableModel  model=new myTableModel();
        table=new JTable();
        table.setModel(model);
        
        table.getColumnModel().getColumn(1).setCellEditor(new MyCustomerEditor());

        table.setCellSelectionEnabled(true);
        scrollPane=new JScrollPane(table);
        topPanel.add(scrollPane,BorderLayout.CENTER);
        
    }
    
    public class myTableModel extends DefaultTableModel
    {
        myTableModel()
        {
            super(dataValues,columnNames);
        }
    
        public boolean isCellEditable(int row,int cols)
        {
            if (cols == 0)
                return false;
            return true;
        }
    }
    
    public static void main(String args[])
    {
      SimpleTest x=new SimpleTest();
      x.setVisible(true);
    }  

    
    public class MyCustomerEditor extends DefaultCellEditor
    {
        private static final long serialVersionUID = 1L;
        int attrLength;
        
        public MyCustomerEditor() 
        {
            super(new JFormattedTextField());
            System.out.println("Inside the editor.");
            final NumberFormat integerFormat = NumberFormat.getIntegerInstance();
            integerFormat.setGroupingUsed(false);
            integerFormat.setRoundingMode(RoundingMode.DOWN);
            integerFormat.setParseIntegerOnly(true);

            final NumberFormatter intFormatter = new NumberFormatter(integerFormat);
            intFormatter.setOverwriteMode(false);

            final JFormattedTextField formattedTextField = (JFormattedTextField) getComponent();
            formattedTextField.setFormatterFactory(new DefaultFormatterFactory(intFormatter));
            formattedTextField.setHorizontalAlignment(SwingConstants.RIGHT);
            formattedTextField.setFocusLostBehavior(JFormattedTextField.PERSIST);
        }
        
        @Override
        public boolean stopCellEditing()
        {
            System.out.println("Stopped editing");
            if (processData())
            {
                return false;
            }

            return super.stopCellEditing();
        }   
        
        boolean processData()
        {
            final JFormattedTextField textField = (JFormattedTextField) super.getComponent();
            final Object o = super.getCellEditorValue();
            final String integer = o.toString();

            System.out.println("Processing Integer value : " + integer);                    
            System.out.println("     textField.getText() : " + textField.getText()); 
            System.out.println("     textField.isEditValid() : " + textField.isEditValid());    
    
            return true;
        }   
    }
}
排查进展

最初因3个测试环境安装的Java版本不一致,怀疑是JDK版本差异导致;后续经测试排除版本因素,确认问题根源是区域设置(Locale)差异:同一台机器仅通过-D启动参数修改Locale配置,即可复现正常/异常两种表现。

解决方案

1. 修复Locale导致的JFormattedTextField负数校验异常

问题出在初始化NumberFormat时没有指定固定Locale,默认会加载JVM当前继承的系统区域配置,不同Locale对负号的识别规则、合法负数格式要求存在差异:部分Locale仅识别本地语言的负号标记、不识别半角-,或是对负号的位置、前后空格有特殊要求,就会把合法的半角负数输入判定为无效。
直接在初始化NumberFormat时强制指定固定Locale即可,比如业务要求统一支持半角负号、阿拉伯数字输入,就指定Locale.ROOT:

// 替换原代码中无参的getIntegerInstance()调用
final NumberFormat integerFormat = NumberFormat.getIntegerInstance(Locale.ROOT);

如果需要适配不同区域用户的输入习惯,可以在校验前先做字符归一化,把全角负号、各区域本地负号统一替换为半角-,再交给Formatter处理。

另外当前代码中processData()方法固定返回true,会导致stopCellEditing()永远返回false,这也是按Enter键无法正常结束编辑的直接诱因,修复校验逻辑后需要调整返回值:输入合法时返回false,让编辑停止流程能正常执行。

2. 不受Locale影响的替代数值校验方案

不要把isEditValid()作为唯一的校验依据,推荐两种稳定性更高的方案:

  • 自定义正则+手动类型转换校验:拿到输入文本后,先用正则匹配业务要求的数值格式(比如支持负整数就用^-?\d+$),匹配通过后手动调用Integer.parseInt()/Long.parseLong()做类型转换,不抛出NumberFormatException就判定为合法。整个逻辑完全自主可控,不会随Locale配置变化。
  • 替换Formatter实现:用MaskFormatter替代NumberFormatter,通过写死输入掩码限定合法格式(比如允许最多6位整数、可选前置负号就设置掩码为"#######"),掩码匹配规则是固定的,不受区域配置影响。

内容的提问来源于stack exchange,提问作者Unhandled Exception

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 06:21:30