为什么firePropertyChange(String propertyName,Object oldValue,Object newValue)受到保护而不是公共?

时间:2014-01-09 23:52:58

标签: java swing event-handling jcalendar

嗯,问题是我正在使用JCalendar库中的IDateEditor接口实现,我注意到Component.firePropertyChange(String propertyName, Object oldValue, Object newValue)方法不公开但受保护。情况如下所示:

public class DateFormattedTextField implements IDateEditor {

    private JFormattedTextField editor;        
    private DateUtil dateUtil;

    ...

    @Override
    public void setDate(Date date) {
        Date oldDate = (Date)editor.getValue();            
        if(dateUtil.checkDate(date)){
            editor.setValue(date);
            editor.firePropertyChange("date", oldDate, date); // <-- error here
        }
    }

}

正如您所看到的,由于此方法受到保护,我无法触发属性更改。当然,如果我让我的课程从JFormattedTextfield延伸而不是使用简单的变量,我可以很容易地摆脱这个问题。

public class DateFormattedTextField extends JFormattedTextField implements IDateEditor {

    private DateUtil dateUtil;

    ...

    @Override
    public void setDate(Date date) {
        Date oldDate = (Date)getValue();            
        if(dateUtil.checkDate(date)){
            setValue(date);
            firePropertyChange("date", oldDate, date); // <-- No problem here
        }
    }
}

但那不是我要问的。我想知道:为什么这种方法受到保护?

我知道它应该是一些设计问题,但我无法弄清楚为什么会这样,特别是考虑到大多数触发属性更改事件的方法都是公开的:

enter image description here

也许最有经验的开发人员可以对此有所了解。提前谢谢。

附录:

到目前为止,这是我的代码。随意使用/修改/播放它。

public class DateFormattedTextField implements IDateEditor {

    private JFormattedTextField editor;        
    private DateUtil dateUtil;
    private DateFormat dateFormat;
    private String dateFormatString;

    public DateFormattedTextField(){
        dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
        editor = new JFormattedTextField(dateFormat);
        editor.setColumns(10);
        editor.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT);
        dateUtil = new DateUtil();            
    }

    @Override
    public Date getDate() {
        return (Date)editor.getValue();
    }

    @Override
    public void setDate(Date date) {
        Date oldDate = (Date)editor.getValue();            
        if(dateUtil.checkDate(date)){
            editor.setValue(date);
            editor.firePropertyChange("date", oldDate, date); // <-- error here
        }
    }

    @Override
    public void setDateFormatString(String dateFormatString) {
        this.dateFormatString = dateFormatString;
    }

    @Override
    public String getDateFormatString() {
        return this.dateFormatString;
    }

    @Override
    public void setSelectableDateRange(Date min, Date max) {
        dateUtil.setSelectableDateRange(min, max);
    }

    @Override
    public Date getMaxSelectableDate() {
        return dateUtil.getMaxSelectableDate();
    }

    @Override
    public Date getMinSelectableDate() {
        return dateUtil.getMinSelectableDate();
    }

    @Override
    public void setMaxSelectableDate(Date max) {
        dateUtil.setMaxSelectableDate(max);
    }

    @Override
    public void setMinSelectableDate(Date min) {
        dateUtil.setMinSelectableDate(min);
    }

    @Override
    public JComponent getUiComponent() {
        return editor;
    }

    @Override
    public void setLocale(Locale locale) {
        editor.setLocale(locale); // to be reviewed
    }

    @Override
    public void setEnabled(boolean enabled) {
        editor.setEnabled(enabled);
    }

    @Override
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        editor.addPropertyChangeListener(listener);
    }

    @Override
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        editor.addPropertyChangeListener(propertyName, listener);
    }

    @Override
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        editor.removePropertyChangeListener(listener);
    }

    @Override
    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        editor.removePropertyChangeListener(propertyName, listener);
    }
}

2 个答案:

答案 0 :(得分:3)

更改通知是任何observable(通常是特别是java bean)的固有责任:如果在更改任何绑定属性时不会触发PropertyChangeEvent,它将违反其合同。既然如此,其他任何一方都不需要使用fireXX方法。

因此,范围比保护范围更广泛没有任何意义。如果您觉得需要它,那么您做错了。

答案 1 :(得分:0)

为了完整起见,我正在添加这个答案。按照@ kleopatra的明智解释和评论,我意识到我在混合概念和责任。在这种情况下,属性更改通知不是用作基础编辑器组件的JFormattedTextField,而是IDateEditor实现本身。

因此,为了完成界面,我使用了PropertyChangeSupport来保存属性更改侦听器列表并通过PropertyChangeEvent通知它们:

public class DefaultDateEditor implements IDateEditor {
    ...
    private final JFormattedTextField editor;
    private final PropertyChangeSupport propertyChangeSupport;
    ...

    public DefaultDateEditor() {
        ...
        propertyChangeSupport = new PropertyChangeSupport(this);
        ...
        editor = new JFormattedTextField();
        ...
    }

    @Override
    public final void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    @Override
    public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
    }

    @Override
    public final void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    @Override
    public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
    }

    public final PropertyChangeListener[] getPropertyChangeListeners() {
        return propertyChangeSupport.getPropertyChangeListeners();
    }

    public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
        return propertyChangeSupport.getPropertyChangeListeners(propertyName);
    }

    protected final void firePropertyChangeEvent(String propertyName, Object oldValue, Object newValue) {
        propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
    ...
}

正如您所看到的,添加/删除PropertyChangeListeners并在属性更改事件上通知他们的任务被委托给PropertyChangeSupport类成员,但事件的来源是this,那是IDateEditor接口实现者。正如@kleopatra所说,这是客户期望成为事件来源的来源,而不是潜在的编辑组件。

以下是结果类的完整代码(对不起扩展名)。如果您愿意,请随意使用/修改或玩它。

/**
 * Custom implementation of {@code IDateEditor} interface. Unlike the default 
 * implementation provided with JCalendar library, this won't allow invalid 
 * inputs of any kind from the user.
 * 
 * @author dic19
 */
public class DefaultDateEditor implements IDateEditor {

    private Date date;
    private String dateFormatString;
    private Locale locale;
    private SimpleDateFormat dateFormat;
    private final DateFormatter dateFormatter;
    private final JFormattedTextField editor;
    private final DateUtil dateUtil;
    private final PropertyChangeSupport propertyChangeSupport;

    public DefaultDateEditor() {
        date = new Date();
        dateUtil = new DateUtil();
        propertyChangeSupport = new PropertyChangeSupport(this);
        addPropertyChangeListener("dateFormatString", new DateFormatStringChangeListener());
        addPropertyChangeListener("locale", new LocaleChangeListener());

        dateFormatString = "yyyy-MM-dd HH:mm:ss";
        locale = Locale.getDefault();
        dateFormat = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale);
        dateFormat.applyPattern(dateFormatString);

        dateFormatter = new DateFormatter();
        dateFormatter.setCommitsOnValidEdit(true);
        dateFormatter.setAllowsInvalid(false);
        dateFormatter.setOverwriteMode(true);

        editor = new JFormattedTextField();
        editor.setValue(date);
        editor.setColumns(15);
        editor.setToolTipText(dateFormatString);
        editor.addPropertyChangeListener("value", new EditorValueChangeListener());

        installFormatterFactoryOnEditor();
    }

    private void installFormatterFactoryOnEditor() {
        dateFormatter.setFormat(dateFormat);
        DefaultFormatterFactory factory = editor.getFormatterFactory() instanceof DefaultFormatterFactory
                                        ? (DefaultFormatterFactory) editor.getFormatterFactory()
                                        : new DefaultFormatterFactory();
        factory.setDefaultFormatter(dateFormatter);
        factory.setDisplayFormatter(dateFormatter);
        factory.setEditFormatter(dateFormatter);
        factory.setNullFormatter(dateFormatter);
        editor.setFormatterFactory(factory);
    }

    @Override
    public Date getDate() {
        return date != null ? new Date(date.getTime()) : null;
    }

    @Override
    public void setDate(Date date) {
        if (dateUtil.checkDate(date)) {
            Date oldValue = this.date;
            this.date = date != null ? new Date(date.getTime()) : null;
            editor.setValue(this.date);
            firePropertyChangeEvent("date", oldValue, date);
        }
    }

    @Override
    public void setDateFormatString(String dateFormatString) {
        String oldDateFormat = this.dateFormatString;
        this.dateFormatString = dateFormatString;
        firePropertyChangeEvent("dateFormatString", oldDateFormat, dateFormatString);
    }

    @Override
    public String getDateFormatString() {
        return dateFormatString;
    }

    @Override
    public void setSelectableDateRange(Date min, Date max) {
        dateUtil.setSelectableDateRange(min, max);
    }

    @Override
    public Date getMaxSelectableDate() {
        return dateUtil.getMaxSelectableDate();
    }

    @Override
    public Date getMinSelectableDate() {
        return dateUtil.getMinSelectableDate();
    }

    @Override
    public void setMaxSelectableDate(Date max) {
        dateUtil.setMaxSelectableDate(max);
    }

    @Override
    public void setMinSelectableDate(Date min) {
        dateUtil.setMinSelectableDate(min);
    }

    @Override
    public JComponent getUiComponent() {
        return editor;
    }

    @Override
    public void setLocale(Locale locale) {
        Locale oldLocale = this.locale;
        this.locale = locale;
        firePropertyChangeEvent("locale", oldLocale, locale);
    }

    @Override
    public void setEnabled(boolean enabled) {
        editor.setEnabled(enabled);
    }

    @Override
    public final void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    @Override
    public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
    }

    @Override
    public final void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    @Override
    public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
    }

    public final PropertyChangeListener[] getPropertyChangeListeners() {
        return propertyChangeSupport.getPropertyChangeListeners();
    }

    public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
        return propertyChangeSupport.getPropertyChangeListeners(propertyName);
    }

    protected final void firePropertyChangeEvent(String propertyName, Object oldValue, Object newValue) {
        propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }

    private class EditorValueChangeListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ("value".equals(evt.getPropertyName())) {
                System.out.println("Old value:" + evt.getOldValue());
                System.out.println("New value:" + evt.getNewValue());
                setDate((Date)evt.getNewValue());
            }
        }
    }

    private class DateFormatStringChangeListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ("dateFormatString".equals(evt.getPropertyName())) {
                dateFormat.applyPattern(dateFormatString);
                editor.setToolTipText(dateFormatString);
                installFormatterFactoryOnEditor();                    
            }
        }
    }

    private class LocaleChangeListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ("locale".equals(evt.getPropertyName())) {
                dateFormat = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale);
                dateFormat.applyPattern(dateFormatString);
                editor.setLocale(locale);
                installFormatterFactoryOnEditor();
            }
        }
    }
}