Die
classes that model different flavors of dice, first exploring issues with constructors, then experimenting with the "types as contracts" idea from class, and finally working with polymorphic methods in the DieRoller
class.
Consider letting the less experienced member of the group do the typing if it seems like there's a mismatch in experience or comfort levels. That will help make sure they don't get left behind. Worst case, flip a coin to see who does the typing. Or have Java generate a random boolean value. (Type (new java.util.Random()).nextBoolean()
in the codepad.)
At the conclusion of the lab, make arrangements for the typer to share a copy of the code with the other member(s) of the group. (E.g. email it, or put it on a shared Google drive, etc.) My solutions to the lab will get posted as well.
BasicDie
class contains the basic functionality of a die — you can create a Die with any number of sides, then roll()
it to get a random result. The HistoryDie
class extends BasicDie
such that it maintains a history (it counts how many times each output value has been produced). CrookedDie
introduces new behavior: Every third roll, an instance of this class "cheats" and rolls the die's maximum value. Create some instances of these classes and experiment with them until you're familiar with their behavior.
BasicDie basic; HistoryDie history; CrookedDie crooked;
Code Legal? basic = new BasicDie(6);
  history = new CrookedDie(6);
  crooked = new BasicDie(6);
  crooked = new HistoryDie(6);
  basic = new CrookedDie(6);
  history = new BasicDie(6);
 
basic = new HistoryDie(6);
which of the following method invocations are legal?
Code Legal? basic.getNumSides()
  basic.printHistory();
  basic.getNumLies()
  basic.toString()
 
history = new CrookedDie(6);
which of the following method invocations are legal?
Code Legal? history.getNumLies()
  history.roll()
  history.printHistory();
  history.getNumSides()
 
crooked = new CrookedDie(6);
which of the following method invocations are legal?
Code Legal? crooked.roll()
  crooked.printHistory();
  crooked.getNumLies()
  crooked.getNumSides()
 
HistoryDie
and recompile the code. Can you still create an instance of HistoryDie
now? Do other constructors run as well? Un-comment the constructor when you're done with this step, so that the code is back to its original state.
CrookedDie
and recompile the code. Can you still create an instance of CrookedDie
now? How is this scenario different from the previous exercise with HistoryDie
? Un-comment the constructor when you're done with this step, so that the code is back to its original state.
CrookedDie
via its one-argument constructor — the one that takes the desired number of sides as a parameter — but it's not possible to create a CrookedDie
using a no-argument (default) constructor. Modify the class so CrookedDie
also has a no-argument constructor that creates a CrookedDie
with six sides. Feel free to modify other classes as well, but try to accomplish the goal without making numSides
public or protected in the BasicDie class.
DieRoller
class and take a look at the rollRepeatedly
method. We know from our discussion of subtyping that while the method's parameter is of type BasicDie
, that really means it will accept an instance of BasicDie
or any of its subtypes. Using parameters in this way is called "polymorphism", and we'll talk more about it in class. Convince yourself that rollRepeatedly
truly is polymorphic by passing it instances of each die class. Below I'm showing what I got when I passed the method a CrookedDie, a HistoryDie, and a BasicDie, in turn. (Lines beginning with ">" are lines I typed into the codepad, with the corresponding outputs below them.)
> DieRoller dr = new DieRoller(); > dr.rollRepeatedly(new CrookedDie(6), 100) 430 (int) > dr.rollRepeatedly(new HistoryDie(6), 100) 372 (int) > dr.rollRepeatedly(new BasicDie(6), 100) 369 (int)
compareDice
, to the DieRoller class. It should take two die instances as arguments, roll each of them 1000 times, and compare the results. Your method should print out the results, including which die instance "won". (In my code I'm including the output from the winner's toString()
method when reporting the winner.)
> DieRoller roller = new DieRoller(); > BasicDie bd = new BasicDie(6); > CrookedDie cd = new CrookedDie(6); > roller.compareDice(bd, cd); The score is 3461 to 4338 Die 2 wins: Die has 6 sides and has rolled 1000 times > roller.compareDice(cd, bd); The score is 4286 to 3447 Die 1 wins: Die has 6 sides and has rolled 2000 times
ArrayList<BasicDie>
and verify that you can actually add instances of any of the three Die classes to the list. If you loop through the list and call roll()
or toString()
, can you tell which versions of the methods are being called?
rollRepeatedly
method to HistoryDie or CrookedDie. Verify that the method accepts instances of the die classes you'd expect.
toString
methods to HistoryDie and CrookedDie that include the class name as part of the output. Verify that compareDice
is calling the expected toString
method when it declares a winner.
CrookedDie
a subclass of BasicDie
instead of HistoryDie
. What functionality does CrookedDie
have now? How does this change the issues above with constructors, and with polymorphism?