-------
--XaX--
---Xb--
-------
The cell marked a has three X's around it,
so it will be born in the next generation.
But that to-be-born cell does not count as a live neighbour of
the cell marked b.
It's as if the all births and deaths for the next generation happen simultaneously. But, of course, you have to compute the changes one at a time. While doing this, you can't simply make immediate updates to theGrid, or your code will not handle examples like the one above properly.
When writing your nextGeneration method, you should focus on the Grid class. You will need to be aware of the variable defined at the top:
Cell [][] theGrid;
since it stores the actual grid which it's your job to update.
When you are working out your nextGeneration algorithm and code,
think about which of those methods that you highlighted might perform
a service that you can use. Almost all of them are not relevant
to you, but at least one is: the evolve method defined in both
LiveCell and EmptyCell. It does a lot of the nextGeneration logic
("if I have 3 neighbours then ..." and so on).
Both LiveCell and EmptyCell have this method,
so if you have a reference to a Cell you can tell it to evolve itself,
even though you don't know which kind of Cell it is.
Java knows, and will therefore call the right evolve method.
Now if you're going to tell a Cell to evolve itself, it needs to be told the number of LiveCells that it has as neighbours. (See the parameter list for evolve.) So you'll have to count up the live neighours first. In order to count up the live neighbours, you need some way to find out whether or not a given Cell is a LiveCell. Say you want to know if Cell (3,8) is live. The best way to do this is to say:
if (theGrid[3][8] instanceof LiveCell) ...
"instanceof" is a keyword in Java. Standish introduces it in appendix A.
Another way to find out if a cell is live is to look at the cell's "ident" variable. This is definitely not as good a way to do it. Think about why not.
X-----
-----X
------
XXX--X