Drag2Shirts |
We went over most of this on Friday, consider this a quick review.
Let's take a look at a more complex program, one that involves regular classes rather than just those corresponding to main programs. The program we will look at is a program to drag two Tshirts, again one that we wrote for Grace earlier.
Let's start by looking at the Tshirt class.
The class starts with two import statements:
import objectdraw.*; import java.awt.*;
Virtually all of our programs will import objectdraw, just as in Grace we used the corresponding dialect. We will also need to import the built-in Java libraries java.awt.* because class Color is defined in that library.
This time our class header does not extend WindowController, similarly to how in Grace, classes that were not main programs did not inherit graphicApplication.
The program has many constants. In Java, Grace's def is spelled private static final. You already know what private means. The keyword final means that the value may not be updated - for variables, you may not assign to it. The static is a little funnier in that it means that there is only one copy of the constant made, and it is shared with all instances of the class. It is a way of saving space when your program is running. Generally, that is the right choice when you are creating instance variables and want a constant. However, if you are making a constant inside a method, then you should just use final and omit the static.
Technically, static means that the identifier is associated with the class rather than the objects generated from the class. Grace does not have a corresponding notion. In Grace, by contrast, if we only want one copy of something we put it in a separate object and share it - kind of like the "empty" objects we used to put in variables that didn't have anything useful in them yet.
The x and y-coordinates used in the objectdraw library are given by type doubles. It is OK to use ints, as they will be converted to type double when necessary, but when you ask an object for a coordinate, it will always return a double.
The instance variables declared in Tshirt are essentially the same as in the Grace Tshirt example. Their types are given by the classes they implement, e.g., FramedRect, FilledRect, FramedOval, etc. Variables that are of object types that are not initialized in their declarations are assigned value null by Java. (Identifiers of primitive types are initialized to 0 or false, depending on their type.) You can think of the value null as being the same as being undefined in Grace. However, you can test the value of a variable to see if it is equal to null in Java, while that is not possible in Grace. If you get a "null pointer" error in Java, it usually means that you have forgotten to initialize a variable that you are using.
By the way, variables that are declared in methods are NOT initialized automatically; the program must assign them a value before they are used or Java will complain.
After the variable declarations, you will find the Tshirt constructor:
public Tshirt(double x, double y, DrawingCanvas canvas) { // create boundary rectangles sleeveTrim = new FramedRect(x, y + NECK_HEIGHT / 2, SLEEVE_WIDTH, SLEEVE_HEIGHT, canvas); ...
The constructor's job main job is to initialize the objects created from the class. The parameters that would be included in the class header in Grace are included in the class constructor in Java. The constructor in Java must have the same name as the class (in this case Tshirt). Parameters are declared with their types as usual, but constructors are not provided with a return type (because they always return an object of the same type as the class). Constructors are invoked with the keyword new:
new Tshirt(50,50,canvas);
While variables can be initialized in their declarations, many are initialized in the constructor. Any other executable code that would have been included in the Grace class definition (outside of methods) will be found in the constructor in Java.
Methods are defined in ways similar to Grace, but with no keyword method. They will generally include the visibility annotation, return type, name, and parameters with their types. Remember there are no multipart method names in Java. Most of the names of methods in the objectdraw library in Java are similar to those in Grace. The most obvious differences are that moveBy is replaced by move and methods of the form attrib :=(...) are instead written in the form setAttrib. For example, rather than writing box.color := red, in Java we write box.setColor(Color.RED). Similarly, rather than writing point.x as in Grace, we write point.getX() in Java. This brings up the important point that requests to parameterless methods in Java must be terminated with (). Finally, the type Point in Grace is written as Location in the Java objectdraw library.
Most of the methods in Tshirt return void (like Done in Grace, but contains returns a boolean. In Grace, a method that returns a value must always include a return before the value. Thus contains has
return body.contains(pt) || sleeves.contains(pt) || neck.contains(pt);
where Grace would simply write
body.contains(pt) || sleeves.contains(pt) || neck.contains(pt);
as the last line of the method.
Finally, you will notice that the move method includes the code
this.move(x - sleeves.getX(), y - neck.getY());
The keyword this is used in Java in place of Grace's self. However it functions in the same way as Grace's self, and, like self, can be dropped as the receiver of a method request.
Let's now take a look at the main program. As usual, it is declared as a class that extends WindowController, and it has a main method that creates a new object from the class and sends it the startController method request.
As usual the class begins with a number of constant (static final) and variable definitions. This time, however, we need to do some initialization. While in most classes (like Tshirt) the initialization code goes in the constructor, classes extending WindowController do not have a constructor. Instead they have a special begin method:
public void begin() {...}
As with the mouse event handling methods, this method must be declared to be public and must have exactly this header for the system to recognize it when the programs starts up.
With this program, the begin method creates new Tshirts that are associated with variables otherShirt and selectedShirt. It also colors one red and the other green. These last statements are a bit more complex than for Grace. In Java, color names, must be prefixed with the name of the class that created them. (They are public constants of the class, hence their names are in all caps.) Second, rather than being able to write otherShirt.color := red as in Grace, Java does not allow programmers to write methods whose name include :=. As a result, methods in the Java version of the objectdraw library usually use a prefix of set rather than tacking := on the end. Thus we can set the color of our shirt by writing otherShirt.setColor(Color.RED). Other substitutions include setX rather than x:=, setWidth rather than width:=, etc.
Finally, recall that assignment statements in Java use = rather than := and all statements are terminated with ";".
The objectdraw names for methods obtaining values from an object are also a bit different from Grace. Rather than just listing the name of a component, the style is to write get before the name of the component. Thus we write getX, getColor, getWidth, etc. A full listing of the Java objectdraw library methods can be found on the course webpages.
Let's now take a look at the mouse handling methods in Java. These should all look very familiar: onMousePress, onMouseDrag, etc. The only differences are those impacting all methods in Java - declaring the visibility (public), the return type (void) is written before the method name, the parameter type comes before the name, and the type Point is replaced by Location.
However, there are also some subtle differences in the way that control constructs are written in Java. For example, keywords after conditions are omitted. Thus we leave out then in if statements and do in while and for loops. Also, while Grace treated elseif as a single word, they must be written as two separate words in Java. Here are examples of if and while statements:
if (cond1) { ... } else if (cond2) { ... } else { ... } while (cond3) { ... }
While it is legal to drop the { } in an if statement or while loop if the block includes only a single statement, it can make your program harder to understand so we strongly recommend it. (Recall that Java does not require proper indenting - though I do - so programs laid out poorly on the page generally can't be easily understood without curly braces.) For loops are more complex in Java, so we will discuss those later.
One last thing before we finish our discussion of this program. The method onMousePress includes the declaration of a local variable, tempShirt. Local variables must be associated with a type, like all other identifiers that are introduced, but they do not have a visibility annotation like private or public. Because the scope of the local variables (like parameters) is limited to the method they are defined in, they are even more limited than private or public, so you may not associate one of those other annotations with them.
Drag2Shirts |