1

我有一个扩展 JScrollPane 的类,它正在创建另一个扩展 JTable 的类的对象。基本上它看起来像这样:

class CustomScrollPane{
   private CustomTable table

   public CustomScrollPane(..){
   table = new CustomTable(this);
   ..
   }
   public void scrollToBottom(){
      ...
   }
}

在 CustomTable 类中,我重写了 tableChanged:

public class CustomTable extends JTable{

private CustomScrollPane scrollPane;

public CustomTable(CustomScrollPane scrollPane){
    super();
    this.scrollPane = scrollPane;
}

@Override
public void tableChanged(TableModelEvent e) {
    super.tableChanged(e);
    scrollPane.scrollToBottom();
}

当我运行它时,我在 tableChanged() 中的 scrollPane 上得到一个 NullPointerException,这怎么可能?scrollPane 在构造函数中设置时如何为空?在调试器中运行它表明 tableChanged() 在构造函数之前被调用。添加条件

     if (scrollPane != null)

实际上解决了这个问题,因为稍后会调用构造函数。此外,将 JTable 定义为其构造,例如:

        table = new JTable(){
        @Override
        public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
            final Component c = super.prepareRenderer(new CustomTableCellRenderer(), row, column);
            if (c instanceof JComponent){
                ((JComponent) c).setOpaque(true);
            }
            return c;
        }

        @Override
        public void paint(Graphics g) {
            int scrolling = scrollPane.getViewport().getViewPosition().y;
            super.paint(g);
            g.drawImage(image.getImage(), -30, -50 + scrolling, null, null);
        }

        @Override
        public void tableChanged(TableModelEvent e) {
            super.tableChanged(e);
            scrollPane.scrollToBottom();
        }
    };

直接在 CustomScrollPane 构造函数中也可以。为什么不能把它分成一个单独的类?

4

2 回答 2

1

看起来JTable构造函数调用方法tableChanged(...)- 这意味着它在您能够初始化scrollPane实例变量之前被调用。

首先,我建议你看看Java Puzzlers一书中的一些谜题 - 特别是谜题 51:有什么意义,也许还有谜题 53:做你的事。他们应该帮助您了解正在发生的事情。基本上,构造函数的第一行CustomTable调用JTable构造函数(通过super())。构造JTable函数试图调用tableChanged- 它已被覆盖。被覆盖的tableChanged尝试操作scrollPane......但所有这些都发生在构造函数 ( super()) 的第 1 行 -该行this.scrollPane = scrollPane执行之前,所以scrollPane仍然为空。

接下来,我建议使用观察者模式。这里有两个对象 - 滚动窗格和自定义表格 - 当另一个对象发生更改时需要通知一个对象。那是教科书的观察者模式。这是粗略的想法:

文件CustomTable.java

public class CustomTable extends JTable {

    // No more scroll pane; only observers
    private List<ChangeListener> listeners = [];

    // no more scroll pagne
    public CustomTable(){
        super();
    }

    @Override
    public void tableChanged(TableModelEvent e) {
        super.tableChanged(e);
        this.fireChangeEvent();
    }

    /* new methods */

    public void addChangeListener(ChangeListener listener) {
        listeners.add(listener);
    }

    public void removeChangeListener(ChangeListener listener) {
        // ...
    }

    private void fireChangeEvent() {
        for(String l : listeners ){
            l.onChange();
        }
    }
}

文件CustomScrollPane.java

class CustomScrollPane implements ChangeListener{
   private CustomTable table

   public CustomScrollPane(/*...*/){
       table = new CustomTable();
       table.addChangeListener(this);
       //...
   }

   public void scrollToBottom(){
      //...
   }

   /* new methods */

  @Override
  public void onChange() {
      scrollToBottom();
  }
}
于 2012-10-13T18:03:18.050 回答
0

这是经典的非常糟糕的模式,让部分初始化的实例逃离构造函数的范围。

我猜当您调用 new CustomTable(this); 时,CustomScrollPane 会触发一些事件到表 CustomTable 中。

并且由于 scrollPane 尚未初始化 - 那么您将获得 NPE。

只是永远不要让事情逃脱构造函数 - 你是安全的。

于 2012-10-13T17:27:18.857 回答