In class this week we discussed abstract classes. An abstract class is missing the implementation of one or more methods, which means it cannot be instantiated (ie: you can't use the "new" operator.
For example, the class Shape
below
is abstract because its two methods
contains
and
draw
are abstract --
these two methods have a declaration part,
but not an implementation part:
public abstract class Shape { ... public abstract boolean contains(int x, int y); public abstract void draw(Graphics g); }Even though we cannot instantiate
Shape
objects with the new Shape(...)
operator,
it is, nonetheless, ok for an abstract class to contain
a constructor. When we extend this class
the constructor will indeed come in handy.
Here was the example we showed in class:
public abstract class Shape { int x, y, width, height; Color color = gray; public Shape(int x, int y, int width, int height) { setBounds(x, y, width, height); } void setBounds(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } public abstract boolean contains(int x, int y); public abstract void draw(Graphics g); }We can extend abstract class
Shape
to create
an instantiable class, such as Box
,
by providing implementations for all of the
abstract methods:
public class Box extends Shape { public Box(int x, int y, int width, int height) { super(x, y, width, height); } public boolean contains(x, y) { return x >= this.x && x < this.x + width && return y >= this.y && y < this.y + height ; } public void draw(Graphics g) { g.setColor(color); g.fillRect(x, y, width, height); g.setColor(Color.black); g.drawRect(x, y, width, height); } }Some terminology: Since
Box
extends Shape
,
Box
is a subclass of Shape
,;
while Shape
is a superclass of Box
.
Note that the Box
class
makes use of
field variables color
,
x
, y
, width
and height
,
which were defined in its superclass Shape
.
Any field variable defined in a class
is also available for use by its subclasses.
Note also that in order to allow the application
program to make shapes with colors other than gray,
you should define the two
"setter" and "getter"
methods
void setColor(Color color)
and
Color getColor()
in your Shape
base class.
These will look a lot like the
void setGift(String gift)
and
String getGift()
methods you defined in a recent
homework assignment.
We can also extend Shape
to create a subclass
Disk
. In this case, the math we would use
to create the contains
method uses
the two field variables cx
and cy
,
which we define in the constructor:
int cx, cy; ... public Disk(int x, int y, int width, int height) { super(x, y, width, height); } // For a disk we need to do a little extra work to set the bounds: void setBounds(int x, int y, int width, int height) { super.setBounds(x, y, width, height); cx = x + width / 2; cy = x + height / 2; }In the
contains
method we
would then implement the following math,
to find out whether a point is inside an ellipse:
public boolean contains(int x, int y) { dx = (x - cx) / (width / 2) dy = (y - cy) / (height / 2) return dx2 + dy2 ≤ 1 }I will leave it to you to turn the math above (the part in italics) into proper Java code. Remember that
dx
and dy
should be declared to be of type double
.
Hint: The first line of your code to do this could be:
double dx = (double)(x - cx) / (width / 2);
You will also need to implement a draw
method for class Disk
.
This looks very much like the draw
method for class Box
, but with
drawRect
and fillRect
replaced by
drawOval
and fillOval
,
respectively.
We can also extend Shape
to create a subclass
Triangle
. In this case, the math we would employ
to create the draw
method uses
the two field arrays X[]
and Y[]
,
to contain the vertices of the triangle,
which we define in the constructor:
int X[] = new int[3], Y[] = new int[3]; ... public Triangle(int x, int y, int width, int height) { super(x, y, width, height); } public void setBounds(int x, int y, int width, int height) { super.setBounds(x, y, width, height); setTriangleVertices(x, y, width, height); } void setTriangleVertices(int x, int y, int width, int height) { X[0] = x; X[1] = x + width / 2; X[2] = x + width; Y[0] = y + height; Y[1] = y; Y[2] = y + height; } ... public void draw(Graphics g) { g.setColor(color); g.fillPolygon(X, Y, 3); g.setColor(Color.black); g.drawPolygon(X, Y, 3); }As we discussed in class, one way to do the math to implement the
contains
method for the
Triangle
class
is to divide the triangle into a left half
and a right half, and to use appropriate
math for each of the resulting
right triangles, to see whether
the point is actually inside the triangle:
public boolean contains(int px, int py) { if px < x + width / 2 dx = (px - x) / (width / 2) dy = (py - y) / height return dx + dy ≥ 1 else dx = (px - (x + width / 2)) / (width / 2) dy = (py - y) / height return dx ≤ dy }I will leave it to you to turn the math above (the part in italics) into proper Java code. Remember again that
dx
and dy
should be declared to be of type double
.
Homework, due Thursday April 14
Your homework assignment is to
create a simple drawing system
as a Java applet
that contains base class Shape
,
and at least the three subclasses
Box
,
Disk
and
Triangle
.
For extra credit, you can
also extend Shape
to other kinds of shapes,
which can be anything you like, including
hexagon, diamond, car, dragon or puppy.
When your applet comes up on the screen,
there should already be some drawing
on the screen, of your own original design.
I recommend, in your applet,
creating an array of Shape
,
something like the following:
Shape myShapes[] = { new Box(100, 100, 300, 100), // body of a car new Disk(125, 150, 100, 100), // rear wheels of a car new Disk(275, 150, 100, 100), // front wheels of a car };
When you do your assignment, refer to the above example for form, not for content. In other words, don't draw a simple car. :-)
Note that in the above example, I have an array
that is declared to have elements of type Shape
,
but I am filling it with objects like Box
and Disk
.
I can get away with doing this because
Box
and Disk
are subclasses of Shape
.
Once they are in array myShapes[]
,
these objects will all appear to be of type Shape
,
but if we call their draw
and contains
methods, they will use their own internal definitions for
those methods, which is exactly what we want.
Your applet should allow users to
drag on any shape to move it to a different position.
I recommend that you do this by
maintaining a field variable, which you can
call something like selectedShape
.
Whenever this object is not null, it means the
user is dragging that object to change its position.
In your render
method, you can look for the mouse
down condition, to see whether the mouse has
landed on a shape:
Shape selectedShape; // this is a field variable ... public void render(Graphics g) { ... if (! wasMouseDown && mouseDown) { selectedShape = null; Loop through all the elements of myShapes[]. If you find one that contains (mouseX,mouseY), set selectedShape to that shape. }Note that in the code you will need to implement above (described in italics) you will need to loop through all of the shapes in your
myShapes[]
array.
Once you find one for which myShapes[i].contains(mouseX, mouseY)
returns true
,
then you should save that shape to a field variable
called something like selectedShape
.
Then in your render
method you can add logic for detecting a mouse drag,
and respond by moving the selectedShape
,
as follows:
if (wasMouseDown && mouseDown) { if (selectedShape != null) selectedShape.setBounds(mouseX - selectedShape.width / 2, mouseY - selectedShape.height / 2, selectedShape.width, selectedShape.height); }For extra credit you might try some of these things, or add features of your own design: