1

JScrollPaneを拡張する1つのクラスがあり、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がコンストラクターで設定されたときにnullになるにはどうすればよいですか?デバッガーで実行すると、コンストラクターの前に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パズルのいくつかのパズルを見てみることをお勧めします。具体的には、パズル51:ポイントとは何か、そしておそらくパズル53:あなたのことをしてください。彼らはあなたが何が起こっているのかを理解するのに役立つはずです。基本的に、コンストラクターの最初の行は(を介して)コンストラクターをCustomTable呼び出します。コンストラクターが呼び出そうとしています-オーバーライドされています。オーバーライドされたものは操作を試みます...しかし、これはすべてコンストラクターの1行目()で発生しています-行が実行されるなので、まだnullです。JTablesuper()JTabletableChangedtableChangedscrollPanesuper()this.scrollPane = scrollPanescrollPane

次に、オブザーバーパターンを使用することをお勧めします。ここには、スクロールペインとカスタムテーブルの2つのオブジェクトがあり、一方が変更されたときに通知を受ける必要があります。それが教科書オブザーバーパターンです。大まかなアイデアは次のとおりです。

ファイル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 に答える