1

I had a bunch of table views that had code duplicated in them. They used to directly inherit the AbstractView class. So then I made them inherit AbstractListView(Which would in it's turn inherit from AbstractView) which would be the new class holding all the common properties for these tables.

Since I made that change this loop is behaving oddly:

for (AbstractViewPanel view : registeredViews) {
        view.modelPropertyChange(evt);
}

Here is the arrayList: registeredViews:

private ArrayList<AbstractViewPanel> registeredViews;

I've run the debugger and the registeredViews arrayList has all of the views including the table ones. For some reason it stalls at the last directly inherited view in the collection and completely skips them.

All my views used to directly inherit:

public class someView extends AbstractViewPanel

Now since my change some of the views are like this:

public class someOtherView extends AbstractListView

Here's AbstractListView:

public abstract class AbstractListView extends AbstractViewPanel

Here's AbstractViewPanel

public abstract class AbstractViewPanel extends JPanel {


public abstract void modelPropertyChange(PropertyChangeEvent evt);

}

The loop is simply not going over anything that inherits AbstractListView, yet I would assume that all of the views in the end are of type AbstractView.

UPDATE I've managed to find a workaround it simply involves changing the loop:

for (int i = 0; i < registeredViews.size(); i++) 
{
        registeredViews.get(i).modelPropertyChange(evt);
}

I would like to know though why the foreach style loop was giving me headaches.

Patrick.SE
  • 4,444
  • 5
  • 34
  • 44
  • How is `AbstractViewPanel` defined? – nhahtdh Jun 24 '12 at 02:47
  • Who? The arrayList is a regular one from Java, I haven't implemented any custom iterator pattern. I'm suspecting a threading problem though right now, the loop doesn't end, it never reaches the end of the function. – Patrick.SE Jun 24 '12 at 02:54
  • What type parameter is specified in the declaration of `registeredViews`. – trashgod Jun 24 '12 at 02:59
  • According to dynamic binding the loop has to call the method also on children classes, did you try placing some `println` in `modelPropertyChange` to check that it is not called? – Jack Jun 24 '12 at 03:04
  • The modelPropertyChange in all the views that inherit AbstractListView are not called. There's something odd though, it iterates over the other views, but when I run the debugger after the last "normal" view iterated it jumps to a different function and never goes back to finish looping over all the elements. The program doesn't crash and runs it's normal course. – Patrick.SE Jun 24 '12 at 03:08

2 Answers2

1

For reference, I've created a skeleton sscce of your design. List<E> implements Iterable<E>, so there's no a priori reason your foreach should fail. Here are a few thing to check:

  • Verify that GUI objects are constructed and manipulated only on the event dispatch thread.

  • Verify that you are not overlooking a ClassCastException when an instance of SomeOtherView is retrieved from the List.

  • As the List is heterogenous, see Bloch, Effective Java 2nd ed., Item 29: Consider typesafe heterogeneous containers." The pattern is mentioned here.

  • Addendum: Would you not see a concurrent modification exception in that case?

    No, ConcurrentModificationException "does not always indicate that an object has been concurrently modified by a different thread." The exception is thrown if the "list has been structurally modified" outside of the iterator, on a best-effort basis. More likely, the failing iterator was obtained after adding some views but before adding others. This could happen unexpectedly if the list were constructed on the initial thread and iterated on the EDT.

SSCCE:

import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Test {

    private List<AbstractViewPanel> registeredViews;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Test().init();
            }
        });
    }

    private void init() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new GridLayout(0, 1));

        registeredViews = new ArrayList<AbstractViewPanel>();
        for (int i = 0; i < 2; i++) {
            registeredViews.add(new SomeView());
            registeredViews.add(new SomeOtherView());
        }
        for (AbstractViewPanel view : registeredViews) {
            f.add(view);
        }

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    abstract class AbstractViewPanel extends JPanel {

        public AbstractViewPanel() {
            add(new JLabel(getClass().toString()));
        }
    }

    abstract class AbstractListView extends AbstractViewPanel {
    }

    class SomeView extends AbstractViewPanel {
    }

    class SomeOtherView extends AbstractListView {
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I'll look into all of this, your answer is pretty complete so I will accept it. I'll make an update when I find the root cause of this. – Patrick.SE Jun 24 '12 at 14:36
1

I believe that your modelPropertyChange changes the registeredViews list.

So when you run the loop using the counter i the loop is limited to the size of the list at the beginning of the loop.

But when you run with the for each loop, if the modelPropertyChange change the list maybe your iterator give you some problem.

dash1e
  • 7,677
  • 1
  • 30
  • 35
  • I thought that at first, but would you not see a concurrent modification exception in that case? – Judge Mental Jun 24 '12 at 04:14
  • Maybe but it'd my most probable solution in this moment, and it is simple to prove: change the foreach loop content, remove `view.modelPropertyChange(evt);` and write `System.out.println("view: " + view);`. It it works, then my answer is true! – dash1e Jun 24 '12 at 09:06
  • I tried, and it does behave like you mention. But my modelPropertyChange don't alter the registeredViews list, I looked at the call hierarchy and I only initially add things to it and loop over it. – Patrick.SE Jun 24 '12 at 14:36
  • +1 for testable hypothesis; more [here](http://stackoverflow.com/a/11174864/230513). – trashgod Jun 24 '12 at 16:02