Into Java - Part XII

From EDM2
Jump to: navigation, search

By Simon Gronlund

Part I

Part II

Part III

Part IV

Part V

Part VI

Part VII

Part VIII

Part IX

Part X

Part XI

Part XII

Part XIII

Part IV

Part XV

Part XVI

Part XVII

Part XVIII

Part XIX

Part XX

Part XXI

Part XXII

Part XXIII


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.

Polymorphism

In Greece there was a god named Morphos that showed himself to sleeping people, using any shape he wished, the Greek word morphos means form . The god Morphos was indeed polymorphic since he had the ability to appear in many (Greek, poly ) forms. In OOP anything that can appear in many forms is polymorphic.

This time we will look into ad hoc polymorphism, or overloading, different signatures are called with a variety of parameter combinations, but still one method name is used. The pure polymorphism will be left to the extreme reader to read himself.

It is not hard to find examples, almost any class in the Java API offers a choice, we pick java.util.Vector that we used the last time. If we start looking at the constructors there are three of them (Java 1.2 adds another one), but they uses exactly the same name, not surprisingly

Vector(int initialCapacity)
Vector(int initialCapacity, int capacityIncrement)

Hence we consider the constructor method polymorphic, you may use it differently under different circumstances. The source code shows that all of them uses each other; if you use the first one it calls the second, but with 10 as default initial capacity, and the second one will call the third with zero as default capacity increment. Naturally, if you call the second one, it will make a call to the third one, again with zero as parameter.

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 Java 1.2 and later
addAll(int index, Collection c) addAll(Collection c) only in Java 1.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 Java 1.2 and later

The important thing to note is that they all use the same method name, but depending on the parameters one of them will be picked by the java compiler upon compiling time. Of course there can be any number of different combinations, I do not know which class and method that has the record. String does certainly not have the record, but still the method valueOf has nine signatures.

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.

Remove shapes

This far we can now paint a few different colored shapes and add these to the canvas. We simply made each shape an object of itself, gave the object a few values to remember, and added the object to a vector. But how can we remove these objects, one at a time? If you have read this column this far you have passed by the method remove of the Vector class. And a few columns back we discussed the listener model.

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.

PaintShape

But first, how will we find the shape clicked? If the shape is any kind of Swing component we could let it listen to itself, but we decided to let the canvas listen. Thus the canvas has to find out which shape we double clicked on, hence we must add a few lines to the PaintShape class. Any PaintShape will be equipped with a method that replies to the question "is this you?", "Do you contain this x,y point?"

Please, look the Java API up, => Index => C => contains </nowiki>; 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 rectvariable and then return its answer. Have a look at this. PaintBox class

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.

public boolean contains(int Xother, int Yother, int w, int h) {
    return (Xother >= myX &&
            Yother >= myY &&
            Xother + w <= myX + myWidth &&
            Yother + h <= myY + myHeight);
}

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.

PaintPanel

Before we start we will look into the MouseEvent class. We know that any action stirs up an event, a mouse action raises MouseEvent. The Java API says:

"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:

  • Mouse Events
    • a mouse button is pressed
    • a mouse button is released
    • a mouse button is clicked (pressed and released)
    • the mouse cursor enters a component
    • the mouse cursor exits a component
  • Mouse Motion Events
    • the mouse is moved
    • the mouse is dragged

That implies a mouse click also generates a couple of mouse-pressed/mouse-released along with a mouse-clicked. That is indeed tricky to take care of and I will not bloat this column with a discussion on that topic. We solve this problem if we only listens to double clicks at the right hand mouse button (that is mouse button 3, since Java is considering mouse button 2 the potential middle button).

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?

PaintBox class

here 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.

PaintBox class

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.

Summary

Overloading, or polymorphism, is a useful concept richly used. A class has more than one method with exactly the same method name but different signatures, different types of parameters sent to it. These methods may use themselves behind the curtain, but the interface is polymorphic.

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.