abstract data types and java classes - computer …epontell/courses/cs272/...cs 272 – fall 2004...

33
. . . . . . . . . . . . . . . . . . . . CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico State University Department of Computer Science

Upload: hathu

Post on 26-Mar-2018

217 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . .. . . .

. . . . . . . . . .

CS 272 – Fall 2004

Abstract Data Types and Java Classes

Enrico Pontelli and Karen Villaverde

New Mexico State University Department of Computer Science

Page 2: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

2

Abstract Data Types Data Types

A Data Type is typically defined as

• a Domain – i.e., a collection of values

• a set of operations that can be applied on the values in the domain

Both these components together characterize a data type. For example, if we consider the data type byte we have that

• the domain contains all the integer number between -256 and +255

• the set of operations includes the operations: addition, multiplication, subtraction, integer division, and modulo. In addition, we have

o operations that are used to compare elements in the domain (e.g., ==, !=, >) and return a Boolean value

Observe that in a data type, the only way to access and use values belonging to the domain is to make use of the operations provided by the data type. The user of the data type does not have any other way of modifying these data. Furthermore, the data type does not tell us anything about how the data are internally represented (e.g., we do not see what bit pattern is used to store a byte object) and we do not see what kind of algorithm is employed to implement each operation.

Thus, when we are dealing with data types, the data type tell us what the values are and what the various operations do, but it does not tell us anything about how all these things are done. This is an example of what is commonly known as Abstraction.

Creating an abstraction means providing clear information about how to use a certain entity, allowing others to use it without the need of knowing anything about how the entity is implemented. We have seen already many examples of Abstraction. Each time we create a method, we perform what is known as Procedural Abstraction. The user of a method needs to know only what the method does and how to call it, without the need to know what statements are present inside the method itself.

A Data Type is another example of abstraction – we are provided a domain and a set of operations to manipulate the values in the domain, but we do not need to know anything about how the data type has been implemented.

The notion of abstraction is typically tied also to the notion of Information Hiding: the developer of an abstraction decided what components of his/her implementation should be

Page 3: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

3

visible to others and which components should be kept hidden. When we write a method, automatically we make available to others information about how to call the method (its name, the description of the parameters, the output type) but nothing else – thus, the body of the method is effectively hidden to the users of the method (e.g., we cannot access or affect the variables that are present inside the method).

The notion of information hiding is a fundamental principle in software development. By carefully choosing which parts of an implementation should be visible to others and which should be kept hidden, we promote a separation between the creation of the implementation and the use of the implementation. Creating this separation is vital to ensure that complex software systems can be built.

Consider what would happen if information hiding was not present, for example in the case of implementing a data type

• if we let people see how the data type is implemented (e.g,, people can see how we are representing the data in memory, and directly access our memory representation), then they will be able to write code that direct access the internal representation of our data, bypassing the operations provided by the data type. As a result, the code written by these users will be dependent on that specific implementation of the data type. If one day we decide to change how the data type is represented (e.g., we discover a more efficient internal representation), even though we support the same set of operations as part of the data type, all the programs written by the users will not work any longer (since they relied on a different internal representation). This prevents the data type to independently evolve.

• if we make all the details of the implementation of a data type visible, we might make very difficult for users to understand how to use it – as they will not see a clear separation between the implementation of the data type and the description of how to use it (which is really the only thing they are interested in).

Thus, what we would really like is the ability to implement a data type, but allow the user to learn only how to use the data type, without being able to access any of the implementation details. If a programming language supports this type of construction, then we say that the language provides an Encapsulation mechanism. This idea is also intuitively illustrated in the figure below; the program cannot see the implementation of the method S, but it only knows how it should be used – it has only a well defined little window to access the method. The method’s specification (i.e., its header) becomes a window to access the method, as well as a “contract”: if you use the method this way, this is exactly what it will do for you.

Page 4: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

4

The “contract” that describes how to use an entity is frequently called the Interface. For a method, the header of the method is its interface.

Abstract Data Types

An Abstract Data Type (ADT) is a data type that has been created by a programmer – i.e., it is not built-in in the programming language. As any other data types, an ADT is composed of a domain (the set of values belonging to the data type) and a collection of operations to manipulate such values. The only difference is that such data type will be constructed by the programmer. When we build an ADT we really want to apply the principles of encapsulation and information hiding mentioned earlier. This means that, once we have finished building the data type, we wish others to use the data type exclusively through the operations we provide, and in no other way. In particular, to protect our implementation and guarantee the ability to evolve software, we want to ensure that the implementation of the ADT is hidden from other users.

Let us make an example. We would like to create an ADT that represents fractions; the domain should be the set of all the possible fractions (since these are the values we are interested in manipulating), and the operations we would like to perform are:

• check if two fractions are equal

• read the numerator and denominator of the fraction

• simplify a fraction

• add and multiply fractions

The result should be a new data type (e.g., called Fraction), so that we can declare variables of this type and use them in our program.

Implementation of method S

Program that uses

method S

Request operation

Result of operation

Page 5: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

5

ADT Specification The first step in the creation of an ADT is the development of its Specification. The specification of an ADT is a precise description (in English) of the ADT. The description should clearly identify what is the domain of the ADT and what are the operations associated to the data type. For each operation, we need to clearly describe what the operation does, what kind of inputs it expects, what kind of result it will produce.

Observe that the specification does not say anything about how we are gong to implement the ADT. It just describes what the ADT does and how it can be used. The people that are interested in using the ADT in their programs (e.g., they need to operate on fractions) need to only read the specification in order to proceed with their programs (THEY DO NOT NEED TO KNOW ANYTHING MORE!). Thus, one can think of the specification of the ADT as the interface to the ADT.

Domain of the ADT We will try to follow a standard pattern in developing the specification of the ADT. The first component of the specification is the description of its domain. The domain is the set of values that we can store in objects of this type. For example:

Fraction ADT

DOMAIN: the set of all the possible fractions

As another example, if we are building an ADT to represent points in the 2-dimensional space, then

Point ADT

DOMAIN: the set of all the points in the two-dimensional space

Note that in both cases we are careful about providing an intuitive description of the set of values, without committing to any specific internal representation (e.g., we do not say how the points are going to be represented internally – they could be represented as Cartesian coordinates or as polar coordinates…).

Operations of the ADT The second part of the specification should describe the operations that belong to the ADT. This is a very delicate part in the development of the specification. We need to ensure that

• We provide all the operations that are needed to use the ADT in a meaningful way (Completeness). Remember that the operations will be the only way to operate on the ADT, thus if we forget an important operation then the users will not be able to use the ADT in their programs.

• We provide only the necessary operations; if we provide too many unnecessary operations, we will end up making the ADT very complicated and hard to use.

For the sake of simplicity, we will typically distinguish four classes of operations

Page 6: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

6

1. Constructors: these operations are used whenever we want to create a new object of that particular type. Constructors are required as the creation of a new object belonging to an ADT requires a sequence of steps

2. Destructors: these are operations that are going to be executed when an object is removed from the system (because it is not used any longer)

3. Inspectors: these are operations that allows one to inspect the content of an object or test properties of the object (without modifying it)

4. Modifiers: these are operations that either modify an object or generate new objects.

Let us illustrate this to the case of the Fraction ADT:

• Constructor: we can assume one constructor that generates a brand new object of type fraction; the operation requires as input the numerator and denominator of the fraction we wish to create. The result should be the new fraction created. We will denote this as follows:

Fraction createFraction (int num, int den)

// creates a new fraction, having num as numerator and den as denominator

// the result is an object of type Fraction

• Destructors: we will ignore this for the moment

• Inspectors: we will consider the following inspectors:

int getNumerator()

// the operation, applied to a fraction, returns the numerator of the fraction

int getDenominator()

// the operation, applied to a fraction, returns the denominator of the fraction

boolean isEqualTo (Fraction f)

Modifiers: we will use the following modifiers:

// the operation, applied to a fraction, return true if the fraction has the same

// value as the fraction f, false otherwise

void simplifyFraction()

// this operation, applied to a fraction, simplifies the fraction to its normal form

void incrementFraction (Fraction f)

// this operation, applied to a fraction, change its value by adding to it the value

Page 7: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

7

// of the fraction f

Let us illustrate another example of an ADT specification. Let us consider an ADT called AppointmentBook. Its specification can be as follows

DOMAIN: the set of all possible appointment books

OPERATIONS:

Constructor:

createAppointmentBook()

// it produces a brand new appointment book, initially empty

Destructor:

none

Inspectors:

boolean isAppointment (Date date, Time time)

// applied to an appointment book, the operation returns true if there is an appointment

// at the specified date and time

String checkAppointment(Date date, Time time)

// applied to an appointment book, the operation returns the textual description of the

// appointment at date and time

Modifiers:

boolean makeAppointment(Date date, Time time)

// the operation adds an appointment at the given date and time; it returns true if the

// appointment is successfully added, false if it fails (because there is already an

// appointment at that date and time)

boolean cancelAppointment (Date date, Time time)

// it removes the appointment at date and time; returns true if it is succesfull, false

// if there is no appointment at that time and date

Page 8: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

8

Implementing ADTs: Java Classes

The previous sections emphasized the specification of an ADT. When you design an ADT, you concentrate on what its operations do, but you ignore how you will implement them. The result should be a set of clearly specified ATD operations.

How do you implement an ADT once its operations are clearly specified? The process requires developing a data structure and a collection to store the values and perform the operations. In order to ensure that the result is an ADT, we need also to enforce the principle of information hiding – i.e., ensure that the users of the ADT are not able to directly access the implementation of the ADT.

For example: let us assume that, in the example of the appointment book, we use an array appt to store the various appointments. If we do not information hiding, then a program could be simply access the elements of the array, e.g., by writing something like

appt[0] = 0

which could destroy the integrity of the implementation of the ADT (e.g., that operation might not make any sense when we talk about appointments, but if the implementation is not hidden nothing prevents the user from doing this type of bad things). Java classes provide us with a mechanism to create information hiding and easily implement ADTs.

Java Classes In the case of an ADT, encapsulation combines data (representing one entity of that type) with the operations that are used to manipulate such data. The resulting entity (data+operations) is called an object. You can think about an object as in the figure below, where the data are hidden and they can be accessed exclusively through the provided methods (operations).

In Java, a class is a new data type, whose instances are objects. A class contains

• data fields

• methods

Data fields and methods are collectively known as class members.

Data

Methods

Request

Results

OBJECT

Page 9: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

9

Methods typically act on the data fields. By default, all members in a class are private, which means that they are not directly accessible by anybody outside of the class. If one wants to make a member accessible from outside (i.e., open a window that allows to access such member), then it should be declared as public. If we use a class to create an ADT, then we will probably want to declare as public the methods that correspond to the operations of the ADT (as we want them to be usable from outside). Note that the methods present inside a class can access all the members of the class, independently from whether they are public or private.

You should almost always declare the data fields of a class as private. If the value of a data field is required outside of the class, then you will need to create a method (public) specifically to read the value of the data field and return it. Similarly, if there is the need of being able to change the value of a data field, then you will need to create a method specifically to perform this task.

In short, an object is a specific combination of data and methods. Classes define the types for objects; hence, objects are frequently referred to as instances of their defining classes.

Some important observations regarding the use of classes to build ADTs:

• In Java, the constructor operation is represented by a special method, which always has the same name as the class. This method is automatically executed each time we request the creation of a new object belonging to the class

• In Java there is an automated mechanism, called garbage collector, which is in charge of discovering objects that are not used any more. This relieves the programmer from having to worry about erasing objects. It is possible to specify a method that will be executed when an object is destroyed; this method is always called finalize.

Let us start with a simple example of an ADT used to describe Spheres. The specification of the ADT is as follows:

DOMAIN: the set of all spheres

OPERATIONS

Constructor:

createSphere(double radius)

// create a new sphere with the specified radius

Destructor: none

Inspectors:

double getRadius( ) // returns the radius of the sphere

double circumference( ) // compute the circumference of the sphere

Page 10: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

10

double area( ) // compute the area of the sphere’s surface area

double volume( ) // compute the volume of the sphere

Modifiers:

void growSphere(double factor) // grow the sphere by the given factor

The implementation of this ADT can be realized by building a class representing the Sphere. In particular

• whenever we create an object of a class, that object will represent one individual sphere

• since each sphere can be represented by simply maintaining its radius, we will introduce one data member in the class, called radius. Note that each time we create one object from the class, that object is going to have its own radius member.

• the various operations belonging to the ADT will be encoded as methods of the class. Since the operations will be executed by whoever wants to use this ADT, these methods will have to be public.

The complete class is described next:

class Sphere { // data member private double radius; // constructor Sphere ( double rad ) { radius = rad; } // inspectors // getRadius: returns the radius of the sphere public double getRadius( ) { return radius; // read the value of the radius and send it out } // compute the circumference of the sphere public double circumference ( ) { return (Math.PI * 2.0 * radius); // compute the circum and send it out } // compute the area public double area ( ) { return ( 4.0 * Math.PI * radius * radius); //sends out the value of the area

Page 11: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

11

} // compute the volume public double volume ( ) { return (4.0 * Math.PI * Math.pow(radius,3.0))/3.0; } // MODIFIERS // grow/shrink the radius by a given factor public void growSphere ( double factor ) { radius = radius * factor; // modify the radius } } // end of the Sphere class

Some comments about this code

• observe that the constructor must have the same name as the name of the class, and no return type; in our example, the constructor requires one argument, the value of the initial radius. This means that each time we create a sphere object, we will need to provide the initial radius as input, otherwise the sphere cannot be created. It is possible to have multiple constructors if one desires so. The different constructors must have all the same name and they can only differ in the parameters they request. In our example, we could have a second constructor that does not have any parameter, which creates a sphere with a default radius. We can accomplish this by adding another method in the class that looks as follows:

// second constructor: creates a sphere with default radius Sphere ( ) { radius = 10.0; }

• note that the methods can freely access, read, and modify the data member radius; this is possible because both the radius data member as well as the methods are part of the same class.

How can we use this class? Let us write another class, containing a main method, which makes use of spheres

class TestSphere { public static void main(String args[ ]) { // create one sphere with radius 5.0 Sphere s1 = new Sphere(5.0); // create another sphere with default radius

Page 12: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

12

Sphere s2 = new Sphere( ); // let us print the radiuses of the two sphere System.out.println(“First sphere has radius” + s1.getRadius() ); System.out.println(“Second sphere has radius “ + s2.getRadius(); // change radius of the second sphere by doubling it s2.growSphere(2.0); // print the volume of the first sphere System.out.println(“The volume of the first sphere is “+s1.volume() ); } } Some comments

• note that we can declare variables of type Sphere; each one is a container that can contain one Sphere. Initially the contain does not contain anything.

• to create a new Sphere, we need to apply the operation new, which creates a new instance of the class. This will also immediately execute the constructor method. In the first sphere (s1) we request the first constructor to be executed, since we are specifying one parameter (to be used as radius). For the second sphere we do not provide parameters, and this will lead to the execution of the second constructor (the one with no parameters).

• each operation has to be applied to a specific object. So if we want to perform the operation getRadius, we need to apply such operation to one specific sphere; this is accomplished using the notation:

s1.getRadius()

which executes the method getRadius within the object s1.

Another example Let us go back to the example of the fraction. The implementation can be as follows:

class Fraction { // data member; they store the num and den private int numerator; private int denominator; // constructor; used to generate a new fraction; need to specify its components public Fraction(int num, int den) {

Page 13: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

13

numerator = num; denominator = den; } // inspectors // Read the numerator public int getNumerator() { return numerator; } // Read the denominator public int getDenominator() { return denominator; } // compare two fractions to see if they are equal public boolean equal(Fraction f) { return ( (f.getNumerator() == numerator) && (f.getDenominator() == denominator) ); } // modifiers // simplify a fraction public void simplifyFraction() { int g; g = gcd(numerator,denominator); numerator = numerator / g; denominator = denominator / g; } // add a fraction to the current one public void addFraction(Fraction f) { denominator = denominator * f.getDenominator(); numerator = numerator * f.getDenominator() +

denominator * f.getNumerator(); } // auxiliary operations; these are used only internally and should not be made // available outside private int gcd (int x, int y) { int i; int g = 1; for (i=2; i <= x && i <= y; i++)

Page 14: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

14

if ( (x % i == 0) && (y % i == 0)) g = i; return (g); } } If we want to write a code fragment that makes use of this new ADT, e.g., to express the fact that we want to compute ¾ + ½ then we can write:

Fraction f1 = new Fraction(3,4); Fraction f2 = new Fraction(1,2); f1.addFraction(f2);

If we want we can also request a simplification of the result:

f1.simplifyFraction();

One aspect we should draw the attention is on the meaning of variables when we are dealing with objects created from a class. Let us first focus on the following operation:

Fraction f = new Fraction(1,2)

what really happen when we perform this operation? The right-hand side of the assignment is simple: when we execute

new Fraction(1,2)

we request the creation of a new object of “type” Fraction (i.e., an object obtained by instantiating the template described by the class Fraction), and once the object has been created we immediately execute its constructor, passing the values 1 and 2 as inputs. The result is the allocation of an area of memory, dedicated to the new object (see the Figure below). Each object has a unique name – it is frequently called the reference of the object. For the sake of simplicity, we have used a number as name of the object in the figure. The number 12423 is the reference (i.e., the name) of that particular object shown in the figure. The system guarantees that each object created has a different reference (a different name).

Why is it important to think about references? Because when we consider

Fraction f = new Fraction (1,2)

Page 15: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

15

then what we are really doing is to store in the variable f the reference to the newly created object. This is extremely important: the variables do not store the object itself, but they simply contain the name of the object.

Consider the following situation:

Fraction f1;

Fraction f2;

f1 = new Fraction (1,2);

f2 = f1;

Now, when we perform the operation f2 = f1 we copy the value currently present inside f1 into the variable f2, so that at the end both have the same value. Since the value of f1 is the name of a certain Fraction object (e.g., the name 1428), then f2 = f1 copies 1428 into the variable f2. In the end, both variables store the same name, which is a reference to the same object. This is illustrated in the following figure.

12423 numerator

denominator

methods

Object

Object Reference

Page 16: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

16

As we can see, the two variables f1 and f2 are really referring to the same object in memory. It is very important to keep this in mind: if we apply a modifier to f1, which changes the content of the object 1428, then the same change will be seen also through f2, since the two are really looking at the same entity in memory.

Consider another example, referring to the Sphere class described earlier. Let us start with

Sphere s1 = new Sphere(3.0);

Sphere s2;

When we perform the assignment

s2 = s1;

we have that both variables are referring to the same object in memory, the sphere with radius 3.0 created in the first instruction. So if we execute

System.out.println(“Radius of s1 is “+s1.getRadius());

System.out.println(“Radius of s2 is “+s2.getRadius());

we will see on the screen the messages

Radius of s1 is 3.0

numerator

denominator

methods

Object 1428

Variables Statements

f1

f2

f1 = new Fraction(1,2)

Memory

f1

f2

1428

f1 = f2 f1

f2

1428

1428 numerator

denominator

methods

Object 1428

Page 17: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

17

Radius of s2 is 3.0

since s1 and s2 are really looking at the same sphere. If now we perform

s2.growSphere(2.0);

this will change the radius of the object referred to by s2 from 3.0 to 6.0; but s2 and s1 are looking at the same object! Which means that if now we execute

System.out.println(“Radius of s1 is “+s1.getRadius());

System.out.println(“Radius of s2 is “+s2.getRadius());

now we will see the following messages:

Radius of s1 is 6.0

Radius of s2 is 6.0

This situation is called aliasing – i.e., we have two or more variables that are referring to the same object in memory.

This also highlights one problem: if we want to perform an operation that copies one object into another object, then this will not happen by doing something like s2 = s1 (we do not create a copy of the object). If we want to be able to create copies of objects, then we typically need to provide a specific method to accomplish this. For example, we might have a method like

public Sphere duplicate( ) { Sphere n = new Sphere(radius); return (n); }

The fact that variables store only references to objects can also cause problems when we try to compare objects for equality. Let us consider the following example:

(*) Fraction f1 = new Fraction (1,2);

(**) Fraction f2 = new Fraction (1,2);

intuitively, the two fractions are equal (if, by equality, we mean representing the same value), they both store the value ½. On the other hand, if we perform the test

f1 = = f2

we will get false as result! Why? Because we are comparing for equality the value of f1 and the value of f2; the value of f1 is the NAME of the Object created in the line (*), while the value of f2 is the NAME of the object created in line (**). Since these are two different objects, they have different names (different references), thus the test for equality fails.

Page 18: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

18

If we want to have a more sophisticated equality test (e.g., we want to declare two fractions to be equal if they have the same numerator and the same denominator), then we need to provide a specific method implementing the test. That’s why we have introduced a method called equal in our Fraction class that does exactly this. This is a common problem: when we build an ADT, typically we have in mind a precise way to compare values of the ADT for equality, and that has to be explicitly implemented using a method in the class.

Similarly, in the case of the Sphere class, we should provide an equal method to provide logical equality between sphere. We could write something like:

boolean equal (Sphere s) { if (s.getRadius() == radius) return true; else return false; }

A Parenthesis: Javadoc

It is important to stress the importance of providing the proper documentation to your code. We will adopt a particular syntax to document our code. At the beginning of the each class we will use some comments to describe the purpose of the class and the author

/**

* This class implements the Fraction ADT

*

* @author Enrico Pontelli

*/

Note that in Java (as in C and C++), anything between a ‘/*’ and a ‘*/’ is treated as a comment. Note the use of the special symbol ‘@author’ in front of the name of the author.

For each method, we will describe its input and output:

/**

* Method equal, used to compare two fractions for equality

*

* @param f This is the fraction we want to compare against

* @return Returns true if f is a fraction with the same value as the current one

*/

Page 19: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

19

If the class that we are creating is declared public, then we will be able to use the command

javadoc

to generate a Web page that contains the documentation of the class.

Let us consider a complete example; the following is a properly described version of our Fraction class.

import java.io.*; /** * Fraction Class; implements an ADT to represent and deal with fractions; * provides key operations addition and multiplication between fractions * * @author Enrico Pontelli * @version 1.0 */ public class fraction { // data member; they store the num and den private int numerator; private int denominator; /** * constructor of fraction class; creates a new fraction with the specified * numerator and denominator * * @param num numerator of the fraction * @param den denominator of the fraction * @return the new fraction object */ public fraction(int num, int den) { numerator = num; denominator = den; } /** * Inspector getNumerator; it returns the current numerator of the fraction * * @return the numerator of the fraction */ public int getNumerator() { return numerator; } /** * Inspector getDenominator; it returns the current denominator of the fraction *

Page 20: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

20

* @return the denominator of the fraction */ public int getDenominator() { return denominator; } /** * Inspector equal; it compares the fraction with the given one for equality * * @param f the fraction we havae to compare against * @return true if the two fractions are identical */ public boolean equal(fraction f) { return ( (f.getNumerator() == numerator) && (f.getDenominator() == denominator) ); } /** * Modifier simplifyFraction; reduces the fraction to its normal form * */ public void simplifyFraction() { int g; g = gcd(numerator,denominator); numerator = numerator / g; denominator = denominator / g; } /** * Modifier addFraction: it takes a fraction and adds it to the current one * * @param f a fraction to be added to the current one */ public void addFraction(fraction f) { denominator = denominator * f.getDenominator(); numerator = numerator * f.getDenominator() + denominator * f.getNumerator(); } /** * Auxiliary method: this is used to compute the gcd of two integers * * @param x first integer * @param y second integer * @return the gcd of x and y */ private int gcd (int x, int y) { int i; int g = 1;

Page 21: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

21

for (i=2; i <= x && i <= y; i++) if ( (x % i == 0) && (y % i == 0)) g = i; return (g); } }

Once we execute the javadoc command on this class, then we obtain an HTML document that describes the class. Here below you can see a part of the Fraction.html document.

Package Class Tree Deprecated Index

PREV CLASS NEXT CLASS FRAMES NO FRAMES All Classes SUMMARY: NESTED | FIELD | CONSTR | METHOD DETAIL: FIELD | CONSTR | METHOD

Class fraction java.lang.Object fraction

public class fraction extends java.lang.Object

Fraction Class; implements an ADT to represent and deal with fractions; provides key operations addition and multiplication between fractions

Constructor Summary fraction(int num, int den) constructor of fraction class; creates a new fraction with the specified numerator and denominator

Method Summary void addFraction(fraction f)

Modifier addFraction: it takes a fraction and adds it to the current one

boolean equal(fraction f) Inspector equal; it compares the fraction with the given one for equality

Page 22: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

22

int getDenominator() Inspector getDenominator; it returns the current denominator of the fraction

int getNumerator() Inspector getNumerator; it returns the current numerator of the fraction

void simplifyFraction() Modifier simplifyFraction; reduces the fraction to its normal form

Methods inherited from class java.lang.Object clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructor Detail

fraction public fraction(int num, int den)

constructor of fraction class; creates a new fraction with the specified numerator and denominator Parameters:

num - numerator of the fraction den - denominator of the fraction

Method Detail

getNumerator public int getNumerator()

Inspector getNumerator; it returns the current numerator of the fraction Returns: the numerator of the fraction

getDenominator public int getDenominator()

Inspector getDenominator; it returns the current denominator of the fraction Returns: the denominator of the fraction

Page 23: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

23

equal public boolean equal(fraction f)

Inspector equal; it compares the fraction with the given one for equality Parameters: f - the fraction we havae to compare against Returns: true if the two fractions are identical

simplifyFraction public void simplifyFraction()

Modifier simplifyFraction; reduces the fraction to its normal form

addFraction public void addFraction(fraction f)

Modifier addFraction: it takes a fraction and adds it to the current one Parameters: f - a fraction to be added to the current one

Package Class Tree Deprecated Index

PREV CLASS NEXT CLASS FRAMES NO FRAMES All Classes SUMMARY: NESTED | FIELD | CONSTR | METHOD DETAIL: FIELD | CONSTR | METHOD

Java Interfaces

Often it is convenient to be able to specify a set of methods that you might want to provide in many different classes. One way to address this situation is through the use of a special construct called Interface. An interface provides a way to specify methods and constants but supplies no implementation details for any of the methods. Interfaces enable you to specify some desired common behavior that may be useful over many different types of objects. You can then design a method to work with a variety of object types that exhibit this common behavior by specifying the interface as the parameter type for the method instead of a class. This allows the method to use the common behavior in its implementation, as long as the arguments to the method have implemented the interface.

Formally, an interface is a collection of method declarations with no data and no bodies. When a class implements an interface, it must implement all of the methods declared in the interface. In this way, the interface enforces requirements that an implementing class have methods with certain specified formats.

Page 24: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

24

Let us consider the problem of describing objects that represent geometric shapes. For all of them we want to have methods that compute the area and the volume. This can be described by an interface

public interface Shape { public double volume( ); public double area( ); }

Now we can start by building a class to represent cubes:

public class Cube implements Shape { private double length;

/** * Constructor of the class; needs the three values * * @param l this is the length of the cube */ public Cube (double l) { length = l; } /** * volume method; computes the volume of the cube * * @return the double representing the volume of the cube */ public double volume( ) { return (length * length * length); } /** * area: computes the area of the side of the cube * * @return returns the area of the side of the cube */ public double area ( ) { return (length * length); } }

Similarly we can define a class for the Sphere

public class Sphere implements Shape

Page 25: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

25

{ private double radius; … }

Inheritance

We will discuss inheritance more extensively in the future. Here we will just provide some simple examples to offer the basic idea. Inheritance is a mechanism provided in Java (and other object-oriented languages) to facilitate code-reuse. The intuition is quite simple: a class is used to describe a template, which can be used to generate objects. Thus, one can look at a class as describing a collection of objects (all the objects that have the same structure). On the other hand, one can frequently identify subsets of this collection of elements that are interesting on their own – to the point that I might be interested in having a specific class describing such subset of elements. For example, consider a class S that defines objects containing a data member x and three methods: a(), b(), and c(). Suppose now we want to identify a class for objects that have the same content as the objects of S, but in addition they have one extra data member (called y) and another method (called d()). One approach is to simply write two separate classes:

class S { int x; a() { … } b() { … } c() { … } } class T { int x; int y; a() { … } b() { … } c() { … } d() { … } } Obviously it would be nice to simply declare the class T by saying: take everything that you have in S, and just add y and d(). Intuitively, the objects of T are special cases of the objects of S (they have the same components, plus something more).

Page 26: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

26

Java allows to do exactly this: declare subclasses; i.e., describe a class by extending an existing one. In Java the description of S and T would be as follows:

class S { protected int x; a() { …. } b() { …. } c() { …. } } class T extends S { int y; d() { … } }

The only important thing to observe is that, in order to make things work properly, we need to declare x as a protected variable – this allows x to be accessed by the methods in the subclass (otherwise, if x is private, then not even the methods of T would be able to use it).

S is called the super-class of T, and T is called a subclass of S.

When we create subclasses, we can not only add elements (data members and methods), but we can also replace methods. E.g., we could have a class

class R extends S { int z; a() { … } } Now, if we have an object of class R and we call the method a on it, then the method defined in the class R will be used (not the a() present in the class S, which has been overridden).

Let us consider a more extensive example; we want to create a class called ColoredSphere, which represents spheres that have a color. We want to create this as a subclass of the Sphere class created earlier.

public class ColoredSphere extends Sphere { private Color color; // Constructor public ColoredSphere(double radius, Color c) { super(radius); color = c; } // inspector that reads the color public Color getColor( )

Page 27: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

27

{ return color; } // modifiers, change the color public void setColor(Color c) { color = c; } }

The interesting thing to observe here is the use of the keyword super in the constructor; the super is used to request the execution of the constructor of the superclass.

It is important to observe that each class created is a subclass of another class. If we do not declare a class as an extension of another one, then it will automatically be created as a subclass of a class called Object.

Let us consider another complete example.

Let us start by defining a class called Shape which implements an ADT to represents entities of type shape. Each object of type shape is described by a position in the 2-d space. Each shape has an area, we want to have the ability to retrieve and modify its position, have to print its description on the screen.

public class Shape { private double xposition; private double yposition; private double area; /** * Constructor – create a new shape located in the origin of the space * * @param a the area of the shape * / public Shape(double a) { xposition =0; yposition = 0; area = a; } /** * Constructor – create a shape at a given coordinate in the space * * @param x this is the x coordinate of the position of the shape * @param y this is the y coordinate of the position of the shape * @param a the area of the shape */ public Shape (double x, double y, double a)

Page 28: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

28

{ xposition = x; yposition = y; area = a; } /** * Inspector getXposition; reads the x coordinate of the shape * * @return the x coordinate of the position of the shape */ public final double getXposition() { return xposition; } /** * Inspector getYposition; read the y coordinate of the shape * * @return the y coordinate of the position of the shspae */ public final double getYposition() { return yposition; } /** * Inspector area; compute the area of the shape * * @return the area of the shape */ public double area () { return area; } /** * Modifier: move the shape to a different position * * @param x new x coordinate * @param y new y coordinate */ public void moveTo (double x, double y) { xposition = x; yposition = y; } /** * Inspector; display a description of the shape * */ public String toString() {

Page 29: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

29

String str = “(X,Y) position: (“+xposition+”,”+yposition+”) \n”; return str; } }

A few observations:

• note that the data members xposition and yposition are declared private (instead of protected, as in the previous example). The reason is that we want these coordinates to be accessible only by the methods in the Shape class; because of this declaration we will not be able to access and modify these data members in the subclasses.

• note that the methods getXposition and getYposition are declared to be final. The meaning of this is the following: we do not allow subclasses to replace these methods. Even the subclasses, they need to use these specific methods to get the coordinates.

• the toString method is a special method that we can introduce in any class; the method takes no arguments and returns a String. The method is automatically called when we perform a System.out.print operation and we give an object of this class as input; the string produced by toString is going to appear on the screen. For example, if we have:

Shape s = new Shape(1.3, 2.6, 10.0);

System.out.println(s);

the message that will show up on the screen is

(X,Y) position: (1.3, 2.6)

Let us now create a class Circle. A Circle is seen as a special type of Shape – thus Circle is going to be a subclass of Shape and will inherit from Shape the basic properties of being located somewhere in space. The Circle is going to have some peculiar additions w.r.t. Shape. A Circle as a radius, and the area is computed in a special way (we do not need to get it explicitely).

public class Circle extends Shape { private double radius; // new data member specific for Circles /** * Constructor; we need to know the radius; by default the Circle is in position 0,0 * * @param r the radius of the circle */ public Circle (double r) { super();

Page 30: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

30

radius = r; } /** * Constructor; in this case we want to specify the initial location of the circle * * @param x initial x coordinate * @param y initial y coordinate * @param r initial radius **/ public Circle (double x, double y, double r) { super(x,y); radius = r; } /** * Inspector: read the radius of the circle * * @return the current radius of the circle **/ public double getRadius() { return radius; } /** * Inspector: read the area of the circle * * @return the area of the circle **/ public double area () { return Math.PI * radius * radius; } /** * Inspector: print the description of a circle * */ public String toString () { String str = “CIRCLE\n”+ “\t”+super.toString()+ “Radius: “+area()+”\n”; return str; } }

Let us make some observations about this class:

Page 31: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

31

• the declaration “extends Shape” tells that all the features of Shape are immediately available also for Circle; thus, for example, we can call the getXposition method for a Circle (even though this method is not defined in the class Circle);

• The constructors contain a call to the method super(); the effect is to request the execution of the method of the superclass. For example, let us consider the first constructor

public Circle (double r) { super(); radius = r; }

The super() call requests execution of the constructor associated to the superclass (i.e., the constructor of Shape). Once the constructor of Shape is finished, then the additional line “radius=r” is executed as well. The constructor of Shape is used to initialize the xposition and yposition components (which cannot be directly accessed from Circle, as they are private to Shape); the extra line is used to initialize the radius data member (which is specific to Circle and not present in Shape).

• Note that the Circle class contains a method called area(), even though such method was already present in Shape. The idea is that the area method present in Shape does not do what we want for Circles, so we want to replace it. We accomplish this by placing a new area method in Circle.

• Observe that we are also replacing the toString method – because we want the description of a Circle to be different than the description of a Shape. Nevertheless, we want the description of a Circle to contain also the information provided by the description of Shape (which shows the position of the Shape). To accomplish this we include a call super.toString() which requests execution of the toString method present in the superclass (i.e., Shape). In general, if we have something of the type

super.method(…)

what this means is that we want to execute the method called method(…) which belongs to the superclass.

Let us now build another subclass of Shape, used to describe Rectangles. This can be defined as follows:

public class Rectangle extends Shape { private double height; private double width; /** * Constructor: create rectangle of given width, height in default (0,0) position * * @param w width of the rectangle

Page 32: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

. . . . . . .. . .

32

* @param h height of the rectangle */ public Rectangle (double w, double h) { super(); width = w; height = h; } /** * Construct; create a rectangle of given height/width in given coordinates * * @param w width of rectangle * @param h height of rectangle * @param x x coordinate of the rectangle * @param y y coordinate of the rectangle */ public Rectangle (double w, double h, double x, double y) { super(x,y); width = w; height = h; } /** * Inspector: read the width of the rectangle * * @return the width of the rectangle **/ public double getWidth() { return width; } /** * Inspector: read the height of the rectangle * * @return the height of the rectangle **/ public double getHeight() { return height; } /** * Inspector: determine the area of the rectangle * * @return the area of the rectangle */ public double area() { return height * width; }

Page 33: Abstract Data Types and Java Classes - Computer …epontell/courses/cs272/...CS 272 – Fall 2004 Abstract Data Types and Java Classes Enrico Pontelli and Karen Villaverde New Mexico

33

/** * Inspector: return the string describing the rectangle * * @return the string that describes the rectangle **/ public String toString() { String str = “RECTANGLE\n”+ super.toString()+ “Width = “+width+” Height=”+height+”\n”; return str; } }