Chapter Eighteen

Advanced Objects; Baton Projectiles*

In this chapter (coauthored with Connelly Barnes) we look at some additional aspects of object-oriented programming (OOP). These aspects are designed to help make programming more efficient by making the reuse of already-written components easier and more reliable. The ideal is to permit this even for entirely different, future projects, for which you will have no memory or knowledge of the internal workings of the already-written components that you want to reuse. OOP concepts are particularly helpful in complicated projects in which you need to add new features without “breaking” the old ones, and in which you may be modifying code that you did not write.

18.1 PROBLEM: TRAJECTORY OF THROWN BATON

In Chapter 12, “Flow Control via Logic,” we examined the motion of a cannonball as it travels through the air, and in Chapter 15 we showed how to include drag. In this chapter we extend our description of frictionless projectile motion to that of a baton that spins as it travels through the air. Figure 18.1 shows the baton as two identical spheres joined by a massless bar. Each sphere has mass m and radius r, with the centers of the spheres separated by a distance L. The baton is thrown with the initial velocity shown on the left of Figure 18.2 (this corresponds to a rotation about the center of the lower sphere).

Problem: Extend the program already developed for projectile motion of a point particle to an object-oriented program that computes the position and velocity of the baton as a function of time. The program should:

image

Figure 18.1 The baton before it is thrown. “x” marks the CM.

image

Figure 18.2 Left: The initial conditions for the baton as it is thrown. Right: The baton spinning in the air under the action of gravity.

1.  plot the position of each end of the baton as a function of time;

2.  plot the translational kinetic energy, the rotational kinetic energy, and the potential energy of the baton, all as functions of time;

3.  use several classes as building blocks so that you may change one building block without affecting the rest of the program;

4.  then be extended to solve for the motion of a baton with an additional lead weight at its center.

18.2 THEORY: COMBINED TRANSLATION AND ROTATION

Classical dynamics describes the motion of the baton as the motion of an imaginary point particle located at the center of mass (CM), plus a rotation about the CM. The CM is located halfway between the spheres and is marked with an X in Figure 18.2. Because the translational and rotational motions are independent, each may be determined separately. This is made easier in the absence of air resistance, since then there are no torques on the baton and the angular velocity ω about the CM is constant.

The baton is thrown with an initial velocity as shown on the left of Figure 18.2. The simplest way to view this is as a translation of the entire baton with a velocity V0, and a rotation of angular velocity ω about the CM. To determine ω, we note that the tangential velocity due to rotation is

image

For the direction of rotation as indicated in the figure, this tangential velocity gets added to the CM velocity at the top of the baton and gets subtracted from the CM velocity at the bottom. Because the total velocity equals 0 at the bottom, and 2V0 at the top, we are able to solve for ω:

image

Figure 18.3 Left: The trajectory (x(t), y(t)) followed by the baton’s CM. Right: The applet JParabola.java showing the entire baton as its CM follows a parabola.

image

If we ignore air resistance, the only force acing on the baton is gravity and, as shown on the right of Figure 18.2, it acts at the CM of the baton. Figure 18.3 shows a plot of the trajectory (x(t), y(t)) of the CM. It is the same parabola as followed by the cannonball in Chapter 12, where we found the position and velocity components of the CM to be:

image

where the horizontal and vertical components of the initial velocity are:

image

Since the gravitational field does not exert any torques on the baton, and since we ignore air resistance, the angular velocity ω = 2V0/L does not change as the baton flies through the air. However, since the CM travels along a parabolic trajectory, the motions of the baton’s ends may appear complicated to an observer on the ground. To describe the motion of the ends, we label one end of the baton a and the other end b, as shown in Figure 18.1.

For a constant angular velocity ω, the angular orientation φ of the baton is

image

where we have taken the initial φ=φ0 =0. Relative to the CM, the ends of the baton are described by the polar coordinates:

image

The ends of the baton are also described by the Cartesian coordinates:

image

The baton’s ends, as seen by a stationary observer, has the vector sum of the position of the CM plus the position relative to the CM:

image

If La and Lb are the distances of ma and mb from CM, the definition of CM tells us that:

image

The moment of inertia of the barbell (ignoring the bar connecting them) is

image

If the bar connecting the masses is uniform with mass m and length L, then it has a moment of inertia about its CM of

image

Because the CM of the bar is at the same location as the CM of the masses, the total moment of inertia for the system is just the sum of the two:

image

The potential energy of the masses is

image

while the potential energy of the bar just has ma +mb replaced by m, since both share the same CM location. The rotational kinetic energy of rotation is

image

with ω the angular velocity and I the moment of inertia for either the masses or the bar (or the sum). The translational kinetic energy of the masses is

image

with ma +mb replaced by m for the bar’s translational KE.

To get a feel for what the motion of a baton actually looks like, we recommend the reader try out the applet JParabola on the CD (applets are discussed in Chapter 21). A picture of its output is given in Figure 18.2, where we see an interestingly complicated motion. The applet is run from a shell with:

% appletviewer jcenterofmass.html

18.3 CS: OOP DESIGN CONCEPTS

In accord with Landau’s First Rule of Education (§ 1.3), we first describe the words used to describe OOP. Object-oriented programming is programming containing component objects with four characteristics [Smit 91]:

Encapsulation: The data and the methods used to produce or access data are encapsulated into entities called objects. For our problem, the data are initial positions, velocities, and properties of a baton, and the objects are the baton and the path it follows. As part of the OOP philosophy, data are manipulated only via distinct methods.

Abstraction: Operations applied to objects must give what is expected to be standard results according to the nature of the objects. To illustrate, summing two matrices always gives another matrix. By incorporating abstraction into programming, we concentrate more on solving the problem and less on details of the implementation.

Inheritance: Objects inherit characteristics (including code) from their ancestors, yet may be different from their ancestors. A baton inherits the motion of a point particle, which in this case describes the motion of the CM, and extends that by permitting rotations about the CM. In addition, we will form a red baton that inherits the characteristics of a colorless baton, and then has the property of color added to it.

Polymorphism: Methods with the same name may affect different objects in different ways. Child objects may have member functions with the same name, but with properties differing from those of their ancestors (analogous to method overload, where the method used depends upon the method’s arguments).

We now solve our problem using OOP techniques. Although it is also possible to use the more traditional techniques of procedural programming, this problem contains the successive layers of complexity that are most appropriate for OOP. We will use several source (.java) files for this problem, each yielding a different class file. Each class will correspond to a different physical aspects of the baton, with additional classes added as needed. There will be class Path.java to represent the trajectory of the CM, a class Ball.java to represent the ball on the end of the baton, and a class (Baton.java) to assemble the other classes into an object representing a flying and spinning baton. Ultimately we will combine the classes to solve our problem.

18.3.1 Including Multiple Classes

The codes Ball.java, Path.java, and Baton.java in Listings 18.1–18.3 produce three class files. You may think of each class as an object that may be created, manipulated, or destroyed as needed. (Recall, objects are abstract data types with multiple parts.) In addition, since these classes may be shared with other Java classes, more complicated objects may be constructed by using these classes as building blocks.

To use class files that are not in your working directory, use the import command to tell Java where to find them.1 For example, in Chapter 11, “Visualization with Java,” we used import ptolemy.plot.* to tell Java to retrieve all class files found in the ptolemy/plot directory. A complication arises here in that multiple class files may contain more than one main method. That being the case, Java uses the first main it finds, starting in the directory from which you issued the java command.

In a project where there are many different types of objects (for example, matrices, vectors, batons, and so on), it is a good idea to define each object inside its own .java file (and therefore its own .class file), and to place the main method in a file such as Main.java or ProjectName.java. This separates individual objects from the helper codes that glue the objects together. And since reading a well-written main method should give you a fair idea of what the entire program does, you want to be able to find the main methods easily.

Listing 18.1 Ball.java

image

18.3.2 Implementation: Ball.java, Path.java Classes

The Ball class in Listing 18.1 creates an object representing a sphere of mass m, radius r, and moment of inertia I. It is our basic building block. Scrutinize the length of the methods in Ball; most are short. Inasmuch as we will be using Ball as a building block, it is a good idea to keep the methods simple and just add more methods to create more complicated objects. Take stock of how similar Ball is to the Complex class in Chapter 16 in its employment of dynamic (nonstatic) variables. In the present case, m and r behave like the re and im dynamic variable in the Complex class in that they too act by being attached to the end of an object’s name. To prove the point, myBall.m extracts the mass of a ball object.

In case you have forgotten, a dynamic variable is one that does not have the word static in front of it (it is also called a nonstatic variable). A static variable does not change its value as it is shared among instances (objects) of the class, and so is static. In contrast, a dynamic variable changes according to the object it modifies, and so may assume different values for each object. As an example, myBall.m may be different for each Ball.

In Ball.java we have defined three dynamic methods, getM, getR, and getI. When affixed to a particular ball object, these methods extract its mass, radius, and moment of inertia, respectively. Dynamic methods are like dynamic variables in that they behave differently depending on the object they modify. To cite an instance, ball1.getR() and ball2.getR() return different values if ball1 and ball2 have different radii. The getI method computes and returns the moment of inertia I = 2/5mr2 of a sphere for an axis passing through its center. The methods getM and getR are template methods; namely, they do not compute anything now, but are included to facilitate future extensions. To name an instance, if the Ball class becomes more complex, you may need to sum the masses of its constituent parts in order to return the ball’s total mass. With the template in place, you do that without having to reacquaint yourself with the rest of the code first.

Look back now and count all of the methods in the Ball class. You should find four, none of which is a main method. This is fine because these methods are used by other classes, one of which will have a main method.

Exercise: Compile the Ball class. If the Java compiler does not complain, you know Ball.java contains valid code. Next try to run the byte code in Ball.class:

image

You should get a message that your program has made an error of the type java.lang.NoSuchMethodError, with the word main at the end. This is Java’s way of saying you need a main method before executing a class file.        ♠

Exercise: Be adventurous and make a main method for the Ball class. Because we have not yet included Path and Baton objects, you will not be able to do much more than test that you have created the Ball object, but that is at least a step in the right direction:

image

This testing code creates a Ball object and prints out its properties by affixing get methods to the object. Compile and run the modified Ball.java and thereby ensure that the Ball class still works properly.        ♠

Listing 18.2 Path.java

image

The class Path in Listing 18.2 creates an object that represents the trajectory [(x(t), y(t)) of the center of mass, (18.3). The class Path is another building block that we will use to construct the baton’s trajectory. It computes the initial velocity components Vox and Voy, and stores them as the dynamic class variables v0x and v0y. These variables need to be dynamic because each new path will have its own initial velocity. In that the acceleration due to gravity g is a constant, it will be the same for all objects, and thus is declared as a static variable (that does not extract values from objects). Survey how Path.java stores g not only as static, but also as a class variable so that its value is available to all methods in the class. The constructor method Path( ) of the class Path takes the polar coordinates (V0, θ) as arguments and computes the components of the initial velocity, (v0x, v0y). This too is a building-block class so it does not need a main method.

Exercise: Use the main method below to test the class Path. Make a Path object and get its properties at several different times. Remember, since this a test code, it does not need to do anything much. Just making an object and checking that it has the expected properties is enough.        ♠

image

18.3.3 Composition, Objects within Objects

A good way to build a complex system is to assemble it from simpler parts. By way of example, automobiles are built from wheels, engines, seats, and so forth, with each of these parts built from simpler parts yet. OOP builds programs in much the same way. We start with the primitive data types of integers, floating-point numbers, and Boolean variables, and combine them into the more complicated data types called objects (what we did combining two doubles into a Complex object). Then we build more complicated objects from simpler objects, and so forth.

The technique of constructing complex objects from simpler ones is called composition. As a consequence of the simple objects being contained within the more complex ones, the former are described by nonstatic class variables. This means that their properties change depending upon which object they are within. When you use composition to create more complex objects, you are working at a higher level of abstraction. Ideally, composition hides the distracting details of the simpler objects from your view so that you focus on the major task to accomplish. This is analogous to first designing the general form of a bridge before worrying about the colors of the cables to be used.

18.3.4 Implementation: Baton.java Class

Now that we have assembled the building-block classes, we combine them to create the baton’s trajectory. We call the combined class Baton.java, given in Listing 18.3, and place the methods to compute the positions of ends of the baton relative to the CM in it. Check first that the Baton class and its methods occupies lines 3–24, while the main method is on lines 25–40. Whether main is placed first or last is a matter of taste, Java does not care—but some programmers do very much. Look next at how the Baton class contains the four dynamic class variables, L, w, path, and ball. Being dynamic, their values differ for each baton, and since they are class variables (not within any methods), they may be used by all methods in the class without getting passed as arguments.

Listing 18.3 Baton.java

image

The subobjects used to construct the baton object are created with the statements

image

These statements tell Java that we are creating the variables path and ball to represent objects of the types Path and Ball. To do this, we must place the methods defining Ball and Path in the directory in which we are creating a Baton. The Java compiler is flexible enough for you to declare class variables in any order, or even pass classes as arguments.

The constructor Baton(Path p, Ball b, …) on line 8 takes the Path and Ball objects as arguments and constructs the Baton object from them. On lines 9 and 10 it assigns these arguments to the appropriate class variables path and ball. We create a Baton from a Ball and a Path such that there is a Ball object at each end, with the CM following the Path object:

30 Baton myBaton = new Baton(myPath, myBall, 0.5, 15.);

Study how the Baton constructor stores the Ball and Path objects passed to it inside the Baton class, even though Ball and Path belong to different classes.

On lines 13–24 we define the methods to manipulate Baton objects. They all have a get as part of their name. This is the standard way of indicating that a method will retrieve or extract some property from the object to which the method is appended. For instance, Baton.getM returns 2m, that is, the sum of the masses of the two spheres. Likewise, the getI method uses the parallel-axes theorem to determine the moment of inertia of the two spheres about the CM, I = 2Im + ½mL2, where m and Im are the mass and moment of inertia of the object about its center of mass. On lines 17–24 we define the methods getXa, getYa, getXb, and getYb. These take the time t as an argument and return the coordinates of the baton’s ends. In each method we first determine the position of the CM by calling path.getX or path.getY, and then add on the relative coordinates of the ends. On line 25 we get to the main method. It starts by creating a Plot object myPlot, a Path object myPath, and a Ball object myBall. In each case we set the initial conditions for the object by passing them as arguments to the constructor (what gets called after the new command).

18.3.5 Composition Exercise

1.  Compile and run the latest version of Baton.java. For this to be successful, you must tell Java to look in the current directory for the class files corresponding to Ball.java and Path.java. One way to do that is to issue the javac and java commands with the -classpath option, with the location of the classes following the option. Here the dot. is shorthand for “the current directory”:

image

The program should run and plot the trajectory of one end of the baton as it travels through the air, and you should end up with a figure like Figure 18.3.

2.  If you want to have the Java compiler automatically include the current directory in the classpath (and to avoid the -classpath. option), you need to change your CLASSPATH environment variable to include the present working directory. We explain how to do that in Chapter 11, “Visualization with Java.”

3.  On line 34 we see that the program executes a for loop over values of t for which the baton remains in the air:

image

    This says to repeat the loop as long as y(t) is positive, namely, as long as the baton is in the air. Of course we could have had the for loop remain active for times less than the hang time T, but then we would have had to calculate the hang time! The weakness in our approach is that the loop will be repeat indefinitely if y(t) never becomes negative.

4.  Plot the trajectory of end b of the baton on the same graph that contains a’s trajectory. You may do this by copying and pasting the for loop for a, and then modifying it for b (make sure to change the data-set number in the call to PtPlot so that the two ends are plotted in different colors).

5.  Use the Print command within the PtPlot application to print out your graph.

6.  Change the mass of the ball variable to some large number, for example, 50 kg, in the Baton constructor method. Add print statements in the constructor and the main program to show how the ball class variable and the myBall object were affected by the new mass. You should find that ball and myBall both reference the same object, since they both refer to the same memory location. In this way changes to one object are reflected in the other object.

In Java, an object is passed between methods and manipulated by reference. This means that its address in memory is passed and not the actual values of all the components parts of it. On the other hand, primitive data types like int and double are manipulated by value:

image

At times we may actually say that objects “are references.” This means that when one object is set equal to another, both objects point to the same location in memory (the start location of the first component of the object). Therefore all three variables myBall, p, and q in the above code fragment refer to the same object in memory. When we change the mass of the ball, all three variables will reflect the new mass value. This also works for object arguments: if you pass an object as an argument to a method and the method modifies the object, then the object in the calling program will also be modified.

18.3.6 Extension: Calculating the Baton’s Energy

Extend your classes so they plot the energy of the baton as a function of time. Plot the kinetic energy of translation, the kinetic energy of rotation, and the potential energy as functions of time:

1. The translational kinetic energy of the baton is the energy associated with motion of the center of mass. Write a getKEcm method in the Baton class that returns the kinetic energy of translation KEcm(t) = mvcm(t)2/2. In terms of pseudocode, the method is:

image

Before you program this method, write getVx and getVy methods that extract the CM velocity from a Baton. Seeing as how the Path class already computes xcm(t) and ycm(t), it is the logical place for the velocity methods. As a guide, we suggest consulting the getX and getY methods.

2. Next we need the method getKEcm in the Baton class to compute KEcm(t). Inasmuch as the method will be in the Baton class, we may call any of the methods in Baton, as well as access the path and ball subobjects there (“subobjects” because they reside inside the Baton object or class). We obtain the velocity components by applying the getV methods to the path subobject within the Baton object:

image

Even though the method is in a different class than the object, Java handles this. Study how getM(), being within getKEcm, acts on the same object as does getKEcm without explicitly specifying the object.

3. Compile the modified Baton and Path classes.

4. Modify Baton.java to plot the translational kinetic energy of the center of mass as a function of time. Comment out the old for loops used for plotting the position of the baton’s ends, and add the code:

image

Compile the modified Baton.java and check that your plot is physically reasonable. The translational kinetic energy should decrease and then increase as the baton goes up and then comes down.

5. Write a method in the Baton class that computes the kinetic energy of rotation about the CM, KEr = ½2. Call getI to extract the moment of inertia of the baton, and check that all classes still compile properly.

6. The potential energy of the baton PE(t) = mgycm(t) is that of a point particle with the total mass of the baton located at the CM. Write a method in the Baton class that computes PE. Use getM to extract the mass of the baton, and use path.g to extract the acceleration due to gravity g. To determine the height as a function of time, write a method path.getY(t) that accesses the path object. Make sure that the methods getKEcm, getKEr, and getPE are in the Baton class.

7. Plot on one graph: the translational kinetic energy, the kinetic energy of rotation, the potential energy, and the total energy. The plots may be obtained with commands such as:

image

Check that all the plotting commands are object-oriented, with myPlot being the plot object. Label your data sets with a myPlot.addLegend command outside of the for loop, and check that your graph is physically reasonable. The total energy and rotational energies should both remain constant in time. However, the gravitational potential energy should fluctuate.

18.3.7 Tutorial: Inheritance and Object Hierarchies

Up until this point we have built up our Java classes via composition, namely, by placing objects inside other objects (using objects as arguments). As powerful as composition is, it is not appropriate for all circumstances. For example, you may want to modify the Baton class to create similar, but not identical, objects such as 3-D batons. A direct approach to extending the program would be to copy and paste parts of the original code into a new class, and then modify the new class. However, this is error-prone and leads to long, complicated, and repetitive code. The OOP approach applies the concept of inheritance to allow us to create new objects that inherit the properties of old objects but have additional properties as well. This is how we create entire hierarchies of objects.

As an example, let us say we want to place red balls on the end of the baton. We make a new class RedBall that inherits properties from Ball by using the extend command:

image

As written, this code creates a RedBall class that is identical to the original Ball class. The keyword extends tells Java to copy all of the methods and variables from Ball into RedBall. In OOP terminology, the Ball class is the parent class or superclass, and the RedBall class is the child class or subclass. It follows then that a class hierarchy is a sort of family tree for classes, with the parent classes at the top of the tree. As things go, children beget children of their own and trees often grow high.

To make RedBall different from Ball, we add the property of color:

image

Now we append the getColor method to a RedBall to find out its color. Consider what it means to have the getR method defined in both the Ball and RedBall classes. We do this because the Java compiler assumes that the getR method in RedBall is more specialized, so it ignores the original method in the Ball class. In computer science language, we would say that the new getR method overrides the original method.

18.3.8 Application: Baton with a Lead Weight

As a second example, we employ inheritance to create a class of objects representing a baton with a weight at its center. We call this new class LeadBaton.java and make it a child of the parent class Baton.java. Consequently, the LeadBaton class inherits all of the methods from the parent Baton class, in addition to having new ones of its own:

image

Here the nondefault constructor LeadBaton(…) takes five arguments, while the Baton constructor takes only three. For the LeadBaton constructor to work, it must call the Baton constructor in order to inherit the properties of a Baton. This is accomplished by use of the key word super, which is shorthand for look in the superclass, and tells Java to look in the parent, or superclass for the constructor. We may also call methods with the super key word; for example, super.getM() will call the getM method from Baton, in place of the getM method from LeadBaton. Finally, because the LeadBaton class assigns new values to the mass, LeadBaton overrides the getM method of Baton.

Exercise: 1. Run and create plots from the LeadBaton class. Start by removing the main method from Baton.java and placing it in the file Main.java. Instead of creating a Baton, now create a LeadBaton with:

LeadBaton myBaton = new LeadBaton(myPath, myBall, 2.5, 15., 10.);

Here the 10. argument describes a 10 kg mass at the center of the baton.

2. Compile and run the main method, remembering to use the “-classpath .” option if needed. You should get a plot of the energies of the lead baton versus time. Compare its energy to an ordinary baton’s and comment on the differences.

3. You should see now how OOP permits us to create many types of batons with only slight modifications of the code. You switch between a Baton and a LeadBaton object with only a single change to main, a modification that would be significantly more difficult with procedural programming.        ♠

18.3.9 Encapsulation to Protect Classes

In the previous section we created the classes for Ball, Path, and Baton objects. In all cases the Java source code for each class had the same basic structure: class variables, constructors, and get methods. Yet classes do different things, and it is common to categorize the functions of classes as either:

Interface: how the outside world manipulates an object; all methods that are applied to that object;

Implementation: the actual internal workings of an object; how the methods make their changes.

As applied to our program, the interface for the Ball class includes a constructor Ball, and the getM, getR, and getI methods. The interface for the Path class includes a constructor Path and the getX and getY methods.

Pure OOP strives to keep objects abstract and manipulate them only through methods. This makes it easy to follow and to control where variables get changed, and thereby makes modifying an existing program easier and less error-prone. With this purpose in mind, we separate methods into those that perform calculations and those that cause the object to do things. In addition, to protect your object from being misused by outsiders, we invoke the private (in contrast to public) key word when declaring class variables. This ensures that these variables may be accessed and changed only from inside the class. Outside code may still manipulate our objects, but it will have to do so by calling the methods we have tested and know will not damage our objects.

Once we have constructed the methods and made the class variables private, we have objects whose internal codes are entirely hidden to outside users, and thereby protected. As authors, we may rewrite the objects’ codes as we want and still have the same working object with a fixed interface for the rest of the world. Furthermore, since the object’s interface is constant, even though we may change the object, there is no need to modify any code that uses the object. This is a great advance in the ability to reuse code and to use other’s people’s codes properly.

This two-step process of creating and protecting abstract objects is known as encapsulation. An encapsulated object may be manipulated only in a general manner that keeps the irrelevant details of its internal workings safely hidden within. Just what constitutes an “irrelevant detail” is in the eye of the programmer. In general, you should place the private key word before every nonstatic class variable, and then write the appropriate methods for accessing the relevant variables. This OOP process of hiding the object’s variables is called data hiding.

18.3.10 Encapsulation Exercise

1.  Place the private key word before all class variables in Ball.java. This accomplishes the first step in encapsulating an object. Print out myBall.m and myBall.r from the main method. The Java compiler should complain, because the variables are now private (visible to Ball class member only), and the main method is outside the Ball class.

2.  Make methods that allow us to manipulate the object in an abstract way; for example, to modify the mass of a Ball object and assign it to the private class variable m, include the line command myBall.setM(5.0). This is the second step in encapsulation. We already have the methods getM, getR, and getI, and the object constructor Ball, but they do not assign a mass to the ball. Insofar as we have used a method to change the private variable m, we have kept our code as general as possible and still have our objects encapsulated.

3.  When we write getM() we are saying that M is the property to be retrieved from a Ball object. Inversely, the method setM sets the property M of an object equal to the argument that is given. This is part of encapsulation, because with both get and set methods on hand, you do not need to access the class variables from outside of the class. The use of get and set methods is standard practice in Java. You do not have to write get and set methods for every class that you create, but you should create these methods for any class you want encapsulated. If you look back at Chapter 11, “Visualization with Java,” you will see that the classes in the PtPlot library have many get and set methods, for example, getTitle, setTitle, getXLabel, and setXLabel.

4.  Java’s interface key word allows us to specify an interface. Here BallInterface defines an interface for Ball-like objects:

image

  This interface does not do anything by itself, but if you modify Ball.java so that public class Ball is replaced by public class Ball implements BallInterface then the Java compiler will check that the Ball class has all of the methods that are specified in the interface. The Java interface and implements commands are useful for having the compiler check that your classes have all of the required methods.

5.  Add an arbitrary new method to the interface and compile Ball. If the method is found in Ball.java, then the Ball class will compile without error.

Listing 18.4 KomplexInterface.java

image

18.3.11 Extension: Complex Number Objects, Komplex.java

In Listing 18.4 we display our design KomplexInterface.java of an interface for complex numbers. To avoid confusion with the Complex objects of Chapter 16, we call the new objects Komplex. We include methods for addition, subtraction, multiplication, division, negation, and conjugation, as well as get and set methods for the real, imaginary, modulus, and phase. We include all methods in the interface and check that javac compiles the interface without error. Remember, an interface must give the arguments and return type for each method.

We still represent complex numbers in Cartesian or polar coordinates:

image

Insofar as the complex number itself is independent of representation, we should be able to switch between a rectangular or polar representation. This is useful because certain mathematical manipulations are simpler in one representation than the other, for example,

image

Listing 18.5 Komplex.java

image

image

Here is our implementation of an interface that permits us to use either representation when manipulating complex numbers. There are three files, Komplex, KomplexInterface, and KomplexTest, all given in the listings. Because these classes call each other, each must be in a class by itself. However, for the compiler to find all the classes that it needs, all three classes must be compiled with the same javac command:

image

We see that KomplexInterface requires us to have methods for getting and setting the real and imaginary parts of Komplex objects, as well as adding, subtracting, multiplying, dividing, and conjugating complex objects. (In comments we see the suggestion that there should also be methods for getting and setting the modulus and phase.)

The class Komplex contains the constructors for Komplex objects. This differs from our previous implementation Complex by having the additional integer variable type. If type = 0, then the complex number(s) are in polar representation, else they are in the Cartesian representation. So, for example, the methods for arithmetic, such as the add method on line 22, is actually two different methods depending upon the value of type. In contrast, the get and set methods for real and imaginary parts on lines 14–21 are needed only for the polar representation, and so the value of type is not needed.

Listing 18.6 KomplexTest.java

image

18.3.12 Polymorphism, Variable Multityping

Polymorphism allows a variable name that is declared as one type to contain other types as a program runs. The idea may be applied to both the class and the interface. Class polymorphism allows a variable that is declared as one type to contain types it inherits. To illustrate, if we declare myBaton of type Baton,

Baton myBaton;

then it is valid to assign an object of type Baton to that variable, which is what we have been doing all along. However, it is also permissible to assign a LeadBaton object to myBaton, and, in fact, it is permissible to assign any other class that inherits from the Baton class to that variable:

image

Polymorphism applies to the arguments of methods as well. If we declare an argument as type Baton, we are saying that the class must be a Baton, or else some class that is a child class of Baton. This is possible because the child classes will have the same methods as the original Baton class (a child class may override a method or leave it alone, but it may not eliminate it).

18.4 KEY WORDS

abstraction

center of mass

child class

composition

data hiding

dynamic methods

dynamic variables

encapsulation

inheritance

interface

multityping

nonstatic variables

object hierarchies

OOP

overriding

parent class

polymorphism

private

programming

reference pass

subclass

subobjects

superclass

templates

translation

rotation

18.5 SUPPLEMENTARY EXERCISES

Use a Java interface to introduce another object corresponding to the polar representation of complex numbers:

image

1.  Define a constructor Complex (r, theta, 1) that constructs the polar representation of a complex number from r and θ. (The 1 is there just to add a third argument and thereby to make the constructor unique.)

2.  Define a method (static or nonstatic) that permits conversion from the Cartesian to the polar representation of complex numbers.

3.  Define a method (static or nonstatic) that permits conversion from the polar to the Cartesian representation of complex numbers.

4.  Define methods (static or nonstatic) for addition, subtraction, multiplication, and division of complex numbers in polar representation. [Hint: multiplication and division are a snap for complex numbers in polar representation, while addition and subtraction are much easier for complex numbers in Cartesian representation.]

1  Actually, the Java compiler looks through all the directories in your classpath and imports the first instance of the needed class that it finds.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset