【问题标题】:Is this cast okay?这个演员阵容好吗?
【发布时间】:2017-05-09 20:54:27
【问题描述】:

我有一个 EventHandler,我在 TextFields 上设置为 event filter。当我编写课程时,我通过在事件上调用 getSource() 并将其转换为 TextField 来获取源 TextField。

EventHandler 的代码:

public class NumberFilter implements EventHandler<KeyEvent> {

    public final int maxLength;
    public NumberFilter(int maxLength) {
        this.maxLength = maxLength;
    }

    @Override
    public void handle(KeyEvent event) {
        TextField textField = (TextField) event.getSource(); //<-- is this cast okay?

        //consume event if there is too much text or the input isn't a number.
        if (textField.getText().length() >= maxLength || !event.getCharacter().matches("[0-9]")) {
            event.consume();
        }
    }
}

按照标准的 java 约定,这种转换可以吗?我怎样才能编写这个类,以便它不能在任何地方使用,除了作为 TextField 的事件过滤器?

【问题讨论】:

  • 您可以编写自定义 TextField,它将使用匿名类或私有类与您的过滤器。
  • @MGorgon 我最初将 EventHandler 作为一个匿名类,但后来我决定我想在其他地方使用它,并将它变成一个自己的类。
  • 根本不要这样做。 (一方面,如果用户通过用鼠标调出上下文菜单来粘贴文本,它无论如何都会完全绕过您的所有检查。)在文本字段上使用TextFormatter

标签: java javafx event-handling conventions


【解决方案1】:

Andy Turner 的回答提供了一种强大的通用方法,允许将事件处理程序添加到仅一种类型的 Node。但是,对于在 TextField(或其他文本输入控件)中否决文本更改的特定情况,使用键事件处理程序的方法不是一个好的方法,原因如下:

  1. 用户可以用鼠标调出上下文菜单并粘贴文本。这根本不涉及任何按键操作,因此不会调用您的处理程序。
  2. 您无法控制文本字段在内部使用哪种类型的键事件。您是否使用KEY_PRESSEDKEY_RELEASEDKEY_TYPED 事件注册此过滤器?您确定文本字段内部使用的事件从一个 JavaFX 版本到下一个版本都保持不变吗?
  3. 您可能会无意中否决键盘快捷键,例如 Ctrl-C(用于复制)或 Ctrl-V(用于粘贴)等。 (如果您不否决“粘贴”的快捷方式,则会允许用户粘贴无效文本的另一个漏洞......)。同样,JavaFX 的未来版本可能会引入额外的快捷方式,这几乎是不可能证明您的功能的。

为了完整起见,此特定用例的首选方法如下:

使用TextFormatter,这是支持的机制,用于否决或修改文本输入控件的文本输入(以及提供格式化或解析控件中文本的机制)。您可以通过在独立类中实现过滤器来使其可重用:

public class NumberFilter implements UnaryOperator<TextFormatter.Change> {

    private final Pattern pattern ;

    public NumberFilter(int maxLength) {
        pattern = Pattern.compile("[0-9]{0,"+maxLength+"}");
    }

    @Override
    public TextFormatter.Change apply(TextFormatter.Change c) {
        String newText = c.getControlNewText() ;
        if (pattern.matcher(newText).matches()) {
            return  c ;
        } else {
            return null ;
        }
    }
}

现在你可以做

TextField textField = new TextField();
textField.setTextFormatter(new TextFormatter<String>(new NumberFilter(5)));

【讨论】:

    【解决方案2】:

    只是为了扩展我对@MaxPower's answer的评论:

    不要使用继承来做你可以用组合更干净地做的事情。

    我认为@James_D 的方法在这种情况下更好;但如果一般来说您想要一个只能添加到特定类型字段的EventHandler,请通过您的 API 强制执行:

    public class NumberFilter implements EventHandler<KeyEvent> {
      public static void addTo(int maxLength, TextField textField) {
        textField.addEventHandler(new NumberFilter(maxLength));  
      }
    
      private NumberFilter(int maxLength) {
        // Private ctor means that you can't just create one of these
        // however you like: you have to create it via the addTo method.
      }
    
      // Now casting in the handle() method is safe.
    }
    

    这样,创建NumberFilter 的唯一方法是通过addTo 方法;这要求您将其添加到TextField

    【讨论】:

      【解决方案3】:

      强制转换是您告诉编译器您比它了解的更多的一种方式。

      如果您知道每次调用这段代码时都会来自 TextField,那就没问题了。否则,我会这样做

      try {
       TextField textField = (TextField) event.getSource();
       //Do Stuff
      }
      catch(ClassCastException e) {
      //handle the error
      
      }
      

      或者如果你想要更多的类型安全

      if(event.getSource() instanceof TextField) {
        TextField textField = (TextField) event.getSource();
      }
      

      或者更好

      public class MyTextField extends TextField implements EventHandler<KeyEvent> {
      
      } 
      

      然后使用 this 代替 TextField 并添加您的方法,然后它是类型安全的。

      【讨论】:

      • “或者更好” Yuk,不。不要为此使用继承,只需将执行强制转换的EventHandler 添加到TextField
      • 为什么不呢?他想要一个只接受文本字段的事件处理程序,在这种情况下他不必为每个文本字段重新实现处理程序。
      • 为什么不呢?因为TextField不是EventHandler,它可能EventHandler
      • 如果你知道强制转换可能会失败,你当然不应该使用 try-catch。使用 instanceof 测试。但正如我在问题下方评论的那样,首先不要为此功能使用事件处理程序。
      • * 不要使用 try { } catch (ClassCastException e) {} - 永远 -。而是使用 instanceof * 如果不是绝对必要,请不要扩展 TextField,请使用阻止事件的事件处理程序。
      猜你喜欢
      • 2015-10-23
      • 1970-01-01
      • 1970-01-01
      • 2012-12-01
      • 2014-10-24
      • 2014-04-05
      • 2010-10-31
      • 1970-01-01
      相关资源
      最近更新 更多