Lecture 1 (1/28): Object-Oriented programming in Java
Classes and Subclasses
Read: Dale, Joyce, and Weems sections 1.3-1.6
Each object O of this class C has, actually, its own
copies of the fields, and, conceptually, its own copy of the methods.
- Define class Rectangle
- Name of file is the same as name of class.
- Fields: xSpan, ySpan. These are specified as protected
meaning they are visible to subclasses, and within this file, but not
- Methods: setSpans, getXSpan, getYSpan, toString. Declare
types of arguments and type returned.
- Constructors. Create an object of the particular type. Constructor
has the same name as the class. The "creation" part is implicit. Can have
more than one constructor with different type signature of arguments.
(For that matter, you can have multiple methods of the same name with
different type signatures of arguments.)
- The default constructor is one with no arguments.
- One constructor can call another constructor by using this(...).
To access the F field of O write O.F
To call the M method of O write O.M()
Inside the class definition of C itself, you can refer to
fields and methods of the object under discussion as just F
For example in Rectangle, in method getXSpan we
refer to field xSpan This means, ``when calling
O.getXSpan(), use O.xSpan.'' Sometimes it is necessary
to use the keyword this so that the compiler knows what you mean.
Note the two calls to the constructors and the call to the method.
Defines LocatedRect as a subclass of Rectangle.
- General principle:
A class hands out information about fields and methods on a
``need to know'' basis. These are private unless there is a
particular reason to make them public
- In particular: data fields are kept private and accessed through
``getter'' and ``setter'' methods. Advantages:
- You can change the underlying data structure, and all you have to
do is change the getter and setters correspondingly. E.g. instead
of using xSpan and ySpan, switch to using diagonal
and aspectRatio. Then all you have to change is to fix the class
Rectangle and make sure the methods within the class do the right
thing, and you can be sure that no code outside the class has to be modified,
since they all using the public methods.
Both of these are to some extent names of prayers.
- Abstract data type: Description of a data structure
in terms of the interaction of the methods, regardless of the fields.
- Forward compatibility.
- You can enforce integrity constraints e.g. that xSpan
and ySpan are not negative.
- You can monitor the use. Particularly for debugging; you can
easily find out when the field is being changed by tracing the setter.
- LocatedRect inherits the fields and methods of
- The first constructor for LocatedRect
calls the default constructor
for Rectangle to create the object and then executes its own
- The second constructor uses super(...) to call the non-default
constructor for Rectangle.
A call to super(...) or to this(...) must be the
first statement in a constructor.
- The definition for method toString() here overrides the
definition in Rectangle.
- The code here can access the fields xSpan and ySpan
of Rectangle because they were declared protected.
If they had been declared private they would not be visible here,
and could only be accessed through public methods of Rectangle.
The assignment Rect3 = LR1 is allowed. Since LR1 is a
LocatedRect, it is also a Rectangle.
The assignment LR4 = R4 is not allowed. R4 is a
Rectangle, but it is not a LocatedRect
The call to Rect3.toString()
uses the toString method for LocatedRect
because that is what Rect3 actually is, rather than
the display method for Rectangle which is how Rect3
is declared. This is known as dynamic dispatching.
C++, by default, makes the opposite choice; that is known as
If you don't define any constructor for a class, then Java automatically
defines a default constructor which creates the object and sets all the fields
to be default values (0 for numeric fields, null for object fields).
If you do define a constructor with arguments for a class, then Java will
not automatically define a default constructor.
Objects and References
A variable of class C is a reference to an object
of class C.
When you execute Rect3 = Rect1, what happens is that
Rect3 becomes a reference to the same object.
So, changing a field of that object changes it in both Rect1
and Rect3 because they are the same thing.
When we execute Rect3 = Rect2 what happens is that
Rect3 now references the same object as Rect2.
Calling new Rectangle creates a new object; it
is the only way to create a new object.
Objects that reference other objects
The father and mother fields are each a
Person. The value is actually a reference to a Person.
The children field is an array of
Person's. The value is actually a reference to an array of
references to Person's.
- The method setParent(P) simultaneously adds P
as the father or mother of this and adds this to
the children of P. In this way, we guarantee (almost --- see the honors
problem, problem set 1) that the record of children is always consistent
with the record of parents; if P is recorded as a parent of
C then C is recorded as a child of P.
This is known as an integrity constraint in database theory.
It is one of the reasons to keep the actual data fields private; by
restricting access to how these are set, the programmer can guarantee
that no other code using Person modifies the children
or father/mother field so as to violate the constraint.
- Note the use of this in setParent
- We are now getting into data structures; in fact, as we will see,
the collection of Persons is a labelled directed graph.
- The variable MaxChildren is a static variable,
meaning that it is associated with the class Person rather than
with individual objects. There is only one for the whole class. If this
were public, and we wanted to refer to it from another class, then we
would write Person.MaxChildren. Writing P.MaxChildren where
P is an object of type Person works, but is less
Parameter passing in Java is always call-by-value. The value of the
actual parameter in the caller is copied into the formal parameter in the
callee, which is a different variable. Reassigning the formal parameter
has no effect on the actual parameter. For instance, when main
calls g(I), the current value 1 of the
actual parameter I is copied into
the formal parameter C. Reassigning C to be 10
has no effect on I .
The same is true of objects passed as parameters. However, the values of
the variables are not the actual objects; they are references
to the object. So when main calls f(W,X) the value
of W is copied into R and the value of X
is copied into S. So R points to the same object
as W and S points to the same object as X
Reassigning S does not affect the value of X
but changing the fields of the object referenced by R does
change the fields of the object referenced by W, since they
are the same object.
The class TestOverload has two methods called show one
for objects of class A and one for objects of class B.
This is called overloading the method name. You can also overload
operators; e.g. + means integer addition, floating point addition,
string concatenation, and so on.
Note that you are allowed to define a class with neither data fields nor
methods. As far as I know, this is only useful for demonstrating language
features; I don't think you ever want to do it in practice.
In TestOverload1 things are trickier because each of the forms
of show has two arguments of different classes.
- The call show(W) uses the version of the method that
matches the class of W and is most specific --- lowest in the
class hierarchy. E.g.
show(X) calls the show for
class A, since X is declared as an A. This is true
even after X is reassigned to refer to an object which is actually
of class B; overloading uses the declared (static) type of
the variable rather than the actual (dynamic) type of the object. This
is the opposite of the way things are done in dynamic dispatching.
show(Y) calls the show for
class B , since Y is declared as an B.
show(Z) calls the show for class B
since there is no show defined for C and B
is the most specific superclass with a defined show method.
- show(X,X) get the first definition, since it is the only
one that fits.
- show(X,Y) gets the second definition, since
X is declared class A,
Y is declared class C, and the type
signature show(A O, B Q) uses the most specific superclasses
- show(Y,X) gets the third definition, by the reverse
- If we try calling show(Y,Y) we get into trouble because the
second and third definitions of show meet this with equal
specificity. So this gives a compiler error.