Since Java is an object-oriented language, here's a quick introduction to OOP (object-oriented programming) before we dive into our first applet. If you're familiar with OOP, or you don't want to bother with it yet, feel free to skip this section and move on to PaintApp.
First of all, what's an object? Well, an object is a structure that contains data, but unlike a struct
in C, an object also contains code that uses its own data.
This concept is very different from traditional procedure-based programming. Before, structures were just programming conveniences: a single structure could hold a bunch of stuff. You'd put related data together into a structure. Then, when you wanted to use that data, you'd write a procedure that accepted your structure as an argument and did something with it. Note that the datayour structureand the code that used that datayour procedurewere separate.
For example, let's say you're working on a drawing program, and you want to create structures that represent circles and rectangles. In C++ notation, you could define them like this:
enum { CIRCLE, RECT };
// ---------- Circle ---------- struct Circle { int x, y; // center int radius; }; void DrawCircle(Circle& circle) { drawCircle(circle.x, circle.y, circle.radius); } // ---------- Rect ---------- struct Rect { int x, y; // upper-left corner int width, height; }; void DrawRect(Rect& rect) { drawRectangle(rect.x, rect.y, rect.width, rect.height); } // ---------- Shape ---------- struct Shape { int type; // enum constant: CIRCLE or RECT union { Circle circle; Rect rect; }; };
Then, when you want to draw a shape, you could call this general-purpose procedure:
void DrawShape(Shape& shape) { switch(shape.type) { case CIRCLE: DrawCircle(shape.circle); break; case RECT: DrawRect(shape.rect); break; }; }
This is quite manageable, for two shapes. However, what if you want to add Line
, Triangle
, and Polygon
shapes? OOP or not, you'll still need to add additional structures. However, you'd also need to define new constants and extend the switch
statement in DrawShape()
to call additional DrawXxxx()
functions. In a larger project, this becomes unwieldy, possibly requiring the recompilation of modules.
In OOP, we combine data and code, creating the Shape
object. The Shape
object has the ability to draw itself, no matter what type of shape it is! This is made possible by the mechanism of inheritance, extending an existing classthe definition of an objectby adding or changing the characteristics of it. Let's see how this is done in pseudo-code. We first define the generic Shape
class:
class Shape { void Draw() {} }
We then extend the Shape
class to form two new classes, Circle
and Rect
:
class Circle extends Shape { int x, y; // center int radius; void Draw() { drawCircle(x, y, radius); } } class Rect extends Shape { int x, y; // upper-left corner int width, height; void Draw() { drawRectangle(x, y, width, height); } }
Finally, here is our new DrawShape()
procedure, for purposes of comparison with the "old way":
void DrawShape(Shape shape) { shape.Draw(); }
Some definitions first: An object's data variables are called its fields. Its procedures and functions are called its methods. In the example above, x
, y
and radius
are fields of the Circle
class, and Draw()
is its sole method.
Explanation time: Because the Circle
class (for example) is extended from Shape
, it is a Shape
, and subsequently inherits all the code of Shape
, which in this case, is the Draw
method. Note that the fields are no longer qualified with the dot operator (e.g. circle.
) inside Draw()
. All the fields of an object are directly available to its methods.
Also, because a Circle
is a Shape
, Circle
can be used anywhere in place of Shape
. Thus, you can write the following:
Circle c; c.x = 10; c.y = 20; c.radius = 5; DrawShape(c);
Next comes the DrawShape()
method. As shown, you call an object's methods by using the dot operator. What might be puzzling though, is the fact that anything might be drawn at all! After all, the Draw()
method in Shape
does absolutely nothing. But OOP will in fact determine at run time the correct method to call, depending on whether shape
is a Shape
, a Circle
, or a Rect
!
Now, suppose you want to support lines in the drawing program. Just define the Line
class as follows:
class Line extends Shape { int x1, y1, x2, y2; void Draw() { drawLine(x1, y1, x2, y2); } }
That's all! The Draw()
method of Line
will be automatically called by DrawShape()
if shape
is a Line
. No new constants, no switch
statement, and no new functionsjust add a new class and you're done!
Whew! What does all this have to do with Java, anyway? As you probably guessed, Java is strictly object-oriented. This means that all the code you write must be part of one class or another. If you've been programming in languages such as C or C++, Pascal, or BASIC, this is a big change. You can no longer write procedures that just "float" around in a module. Even the main
function in stand-alone applications must be part of a class!