Casting changes the type of an object to something more specific than it is currently. For example, from a general real number to a specific integer number or from a generic Object to a more specific GP.Shape. Less formally, it is the act of telling Java to trust that we know the type of the expression better than it does. Because a collection needs to be able to hold any kind of object, it cannot make any assumptions about what has been put into it. The only way to do this is to return the most generic thing possible in Java: an Object. However, we cannot tell a generic Object to SetColor, only GP.Shapes. Thus, since we know shapes were put into the collection, we can cast the result returned from the collection to a shape in order to tell it to change its color.((GP.Shape)myShapes.GetRandomElement()).SetColor(new GP.Attributes.Colors.Random());
Consider the following program that demonstrates a ball bouncing around within an applet by adding three small behaviors to a single shape rather than using one more complex behavior (as most of you did in Pong):
Explain how this version works. Is one easier to understand than the other? In particular, what difference does the order the behaviors are added matter or whether each behavior shares the same GP.Attributes.Vector or creates its own separate copy.
The applet adds three behaviors to an oval: the first makes it move forever according to the given velocity; the second changes the direction of that velocity only if the shape's x-coordinate is outside the applet; and the third changes the direction of that velocity only if the shape's y-coordinate is outside the applet. Arguably, this version is easier to understand because each of the behaviors is extremely simple --- only one or two lines of code. However, it can be difficult to predict perfectly how they will all work together and one might need to be careful when adding more behaviors to the shape. This is the difference between a centralized program, where all the logic is in one place, and a decentralized program, where the logic is spread over many places. For example, the questions of order and sharing would not even come up in the original version because both are hard-coded into the program.
Since they all step at the same rate, i.e., with the same delay, it would be hard to detect any visible difference in the shapes movement if the MoveBehavior is added first or added last. However, if the MoveBehavior is added in between the two checkng behaviors, then the ball will move differently only when it hits a corner. In the original case, the ball bounces directly back into the applet the way it came. In the case where the checking behaviors are split, a move happens between the checks causing the ball to move slightly out of the corner. Over time this causes the ball to follow a very different path than the original case.
Finally, this example would not work at all if all three behaviors did not share the same vector. If each behavior had its own separate copy, it would be as if the checking behaviors did not even exist! Whatever initial velocity was given to the MoveBehavior would remain unchanged forever, causing the ball to move off the applet and never come back. Once outside the applet, the checking behaviors would constantly change their own velocity, but it would have no affect on the copy used by the MoveBehavior.
public class ColorRandomly extends GP.Reactions.MouseClicked
{
GP.Shape myTarget;
public ColorRandomly (GP.Shape target)
{
myTarget = target;
}
public void React ()
{
myTarget.SetColor(new GP.Attributes.Colors.Random());
}
}
public class Counter extends GP.Reactions.MouseClicked
{
GP.Attributes.Values.Counter myTarget;
public Counter (GP.Attributes.Values.Counter count)
{
myTarget = count;
}
public void React ()
{
myTarget.Increment(1);
}
}
public class ColorAlternately extends GP.Reactions.MouseClicked
{
GP.Shape myTarget;
GP.Collections.Sequence myColors;
GP.Attributes.Values.Counter myNumClicks;
public ColorAlternately (GP.Shape target, GP.Collections.Sequence twoColors)
{
myTarget = target;
myColors = twoColors;
myNumClicks = new GP.Attributes.Values.Counter(0);
}
public void React ()
{
myNumClicks.Increment(1);
if (myNumClicks.GetValue() % 2 == 0)
{
myTarget.SetColor((GP.Attributes.Color)myColors.GetElementAt(0));
}
else
{
myTarget.SetColor((GP.Attributes.Color)myColors.GetElementAt(1));
}
}
}
public class ColorSequentially extends GP.Reactions.MouseClicked
{
GP.Shape myTarget;
GP.Collections.Sequence myColors;
GP.Attributes.Values.Counter myNumClicks;
public ColorSequentially (GP.Shape target, GP.Collections.Sequence colors)
{
myTarget = target;
myColors = colors;
myNumClicks = new GP.Attributes.Values.Counter(0);
}
public void React ()
{
myNumClicks.Increment(1);
double index = myNumClicks.GetValue() % myColors.NumElements();
myTarget.SetColor((GP.Attributes.Color)myColors.GetElementAt(index));
}
}