16 December 2000 Simon Gronlund is earning his Master of Science in Computer Science at the Royal Institute of Technology, Sweden, as an adult student. He also teaches Java and computer-related courses at the college. When he isn't tampering with his Warp 4 PC, he spends his spare time with his two boys and his wife. If you have a comment about the content of this article, please feel free to vent in the OS/2 eZine discussion forums |
|
Into Java, Part 12
Last time we discussed how to override
a method, an OOP (Object Oriented Programming) technique commonly used, a method
that override another is a method in one or several subclasses that hides access
to a method implemented in a superclass. Overriding is also sometimes named shadowing,
a method shadows another one, hides that one. But since shadowing also has another
meaning, a lot more used, I do never use that term on overriding. This time we continue
the theory part of these columns with a brief discussion on overloading and polymorphism. |
Is that bad or good? I consider it
good, it is always good to have some choices to pick from. You are the one to make
a decision from the very beginning, which one to pick. I will not discuss that part
to much, but if you from the beginning know that the resulting
Vector will be of a certain
amount, make it that big then, with a 10% extra for growth. If you know that it
will grow very slowly, set the increment capacity to something reasonable, else
the Vector will
double itself every time it comes to the upper limit.
Does a
Vector have an upper limit?
Yes, since under the hood it simply is an array, an Object[], and arrays can not grow, but the Vector has
some helper methods hidden to take care of that. Not to stray away from the topic,
polymorphism, I promise to dig deeper into the important area of data structures
next time. (Yes, I know I said there would be a look under the hood on Vector, but
due to my studies and personal matters I have not had the time to write that much.
I am sorry!)
There are other examples of polymorphism
in the Vector class, a few examples are:
add(int index, Object element) |
|
add(Object o) |
only in Java1.2 and later |
addAll(int index, Collection c) |
addAll(Collection c) |
only in Java1.2 and later | |
indexOf(Object elem) |
indexOf(Object elem, int index) | ||
lastIndexOf(Object elem) |
lastIndexOf(Object elem, int index) | ||
remove(int index) |
remove(Object o) |
only in Java1.2 and later | |
toArray() |
toArray(Object[] a) |
only in Java1.2 and later | |
|
Whenever you create a class and figures
out that a method can or should be called with different arguments from time to
time, overloading can be the way to solve the issue. It is always easier to remember
only a few method names, so make the few methods accept the parameters reasonable
under several signatures.
Think of a listener that will respond
to a double click on a shape, if we clicked a shape it shall be removed from the
vector. The question is, who shall act as the listener? The shapes? The canvas?
Or the application itself? Any one is possible, but a few things lead us to pick
the PaintPanel;
it has a MouseAdapter to
use, and it holds the storage of the shape objects. Let us add this feature to the
PaintPanel then.
Please, look the Java API up, => Index => C => contains ;
you find a lot of contains there. All of them do the job we would
like to, but which one is best suited? So far "none"! That is because
our PaintShape does
not inherit from one of these classes seen. But one of them may be used, the java.awt.Rectangle. Rectangle is
a humble class that corresponds to a rectangular area and can answer our question
(any shape we make can be said to cover a rectangular area).
Our problem so far is that there
is no Rectangle in PaintShape,
let us add one, a simple declaration
private Rectangle rect;among the other declared variables. Now we must figure out were to instantiate it. The obvious place is when we learn to know the stop coordinate, since we know the starting point from the very beginning but the final point is not known until the complete shape is set up. That is made in the setStop method. Further you see in the Java API that a Rectangle is most easily constructed with x, y, width and height. And we would not like to do the mistake of using negative values to width and height, don't we? The code is found beneath.
How to answer the questions? Most
easily we pass the question to the rect variable and then return its answer. Have
a look at this.
|
There is no problem declaring variables
of the same data type at the same line as done here, but I do not recommend it since
you can not comment them if you like to, hence it is not a good coding style. Anyway,
as is done in the paintComponent method we must avoid negative width and height values,
hence the tests. Further we create a somewhat bigger rectangle than the shape itself,
only to be useful if it is a very tiny shape hard to double click. From now on any PaintShape recognizes
its area and remember it in a special variable.
Is it good to hold a variable such
as this one? Doesn't it eat a lot of memory? To the first question, it is mostly
good, but the judge is you. And, yes it eats memory, not much each shape but that
is true. The opposite to use memory is to use CPU time, since if there is no Rectangle to
ask we have to make one each time asked, passing the question to that temporarily
constructed one. Hence each question will take more time.
Of course this is half-truth!!! We
could have built ourself that valuable method contains
ourselves, with no need
for the Rectangle.
But then we must consider the pros and cons with using a pre-built class' method.
Or spending our time writing the code ourselves. Both times it is about time--CPU
or our time--versus eating memory. Admittedly the code is not hard to write, thus
I give it here, but the next time it might not be this easy.
This code is general code, not adapted
to the PaintShape class,
but that is easily done. To figure out how it works, sketch a few points inside
and outside a rectangle sketched on a squared paper. Remember that ANDed clauses
have to be true every one of them to return true, but OR is pleased with only one
to be true.
The PaintShape class is done, with the Rectangle or with the code above that of course is the most economic one. Still you have to make yourself proper widths and heights. public boolean contains(int Xother, int Yother, int w, int h) { return (Xother >= myX && Yother >= myY && Xother + w <= myX + myWidth && Yother + h <= myY + myHeight); }
"An event which indicates that a mouse action occurred in a component. This
event is used both for mouse events (click, enter, exit) and mouse motion events
(moves and drags).
This low-level event is generated
by a component object for:
The PaintPanel
class shall listen to mouse
clicks, and it is prepared to do the task since we use the
MouseAdapter from earlier
columns. In the MouseAdapter class there is a method mouseClicked
that we haven't used so
far, but now we add it within the addMouseListener code block, as the other two are. A mouse
click is a down-up within a short time slice. But we are looking for a double mouse
click, how do we do then?
|
There are two tests, the first one
fetches the click count, understood as the number of consecutive clicks considered
as double clicks (that speed is adjustable at the Mouse object in the OS/2 System
folder (or whichever operating system you use)). If the click count is more than
1 click, we consider it a double click. The next test is analogous to the other
two event catchers, listen only to the chosen button using a button mask.
If passing the test we continue with
fetching the mouse coordinates and we construct a java.awt.Point
out of them, a convenience
class holding x and y. That new point we pass to another method that we have to
build. What are the prerequisites of that method?
The method
removeShape is called with
a Point as
parameter. The purpose is to find the shape held in storedShapes
(the vector holding all
the shapes) that contains that Point. Every time we are thinking of how to find
anything held in a vector, or an array for that matter, we have to think "a
for-loop" or at least "a while-loop". That is since we are to looking
at each object held, from the first one to the last, a job suitable for a loop.
Further remember that a Vector does
not think of which type of object is added to itself, everything is considered of
the class Object.
(With the restriction that primitive data types can not be added, they have to be
wrapped in their counterpart classes respectively.) Since everything is considered
an Object we
have to tell javac that
we know that there is only PaintShape objects added, by casting the objects before
we assign them to tempS.
|
The for-loop will pick up every object
stored in storedShapes,
next we have to find out if the Point is inside the rectangular area of tempS. If
that test receives a true, tempS.contains(xy) is true, we call the convenient method removeElement(Object obj) of
the Vector class.
And that is it. Only a call to repaint so the canvas is to be repainted as soon as
Java finds it suitable.
Finally a
break statement since we
do not need to continue the loop any longer. The break is always breaking the innermost
loop it is found inside. That loop only. The loop is of the common kinds, for-loop,
while-loop or the do/while-loop. If you forget the break
statement the loop will
continue until the end of the vector or the array is found, that is, you might use
a lot of CPU time if the object looked for was found quickly but you continue the
search.
A final word on the
repaint. Yes, sometimes
it does not work! I do not know exactly why, it seems like the call is lost or ignored.
The result is that the shape you liked to remove is still there--until you do your
next move when it is properly erased from the canvas. It was removed from the vector
as it should, but the canvas was not updated. I have tried many tricks but to no
benefit, and the dirty tricks should not be shown in a column like this one. Anyone
giving me an explanation?
Look out now, you are removing the
first shape found in the vector, that might be the wrong one if
their rectangular areas overlap each other. The point double clicked might be contained
within two or more shapes. But that is another problem. If you like to you may add
a button to the TopButtonPanel, or elsewhere, that calls the
removeAllElements method
of the storedShape (the Vector class).
That is easily done, go ahead.
|
We added one class or another to
our repertoire, Point and MouseEvent (used
in earlier installments). A few new methods were found and used. Finding objects
was discussed. And some small digressions are done. I now consider most of the PaintBox finished,
we may come back in the future, but now it will rest for a while.
Next time I will discuss data structures.
If you read my view on Java 2 and Java
2 SDK, version 1.3, you read that these versions added a plethora of such stores
to Java. Unfortunately Java 1.1.8 is not that well equipped, but we will do our
best anyway.
|
|
|