COMP6700: Lectures Block 2

Alexei B Khorev

March 2013

Topics of the block 2

What we will do in this part:

  1. OO paradigm: Classes and Objects

  2. Constructors

  3. Scope and access modifiers

  4. Inheritance

  5. Polymorphism and dynamic binding

  6. Abstract classes and Interfaces

  7. Overriding and hiding

  8. equals() and clone()

  9. Wrapper classes and Auto In-boxing/Un-boxing

  10. Class Design

Procedural Paradigm

The procedural (structural) programming paradigm, which is what we follow when writing separate methods and then calling them from the main method, divide the virtual world into three distinct parts:

Data and Operations are very different:

The result: Supreme Being is overburdened, He must be truly omniscient, know and control everything ever happening in Virtual World. This is hard, and getting harder (complexity of software grows). Supreme Being is unwell (software industry crisis).

Easing the Burden in OOP

Poor old man (or is it “Poor us programmers”?)

creator

(from “Emperor’s New Mind” by R. Penrose)

Object-Oriented Paradigm

“This division [between data and operations] is grounded in the way computers work, so it’s not one that you can easily ignore or push aside. Like the equally pervasive distinctions between matter and energy and between nouns and verbs, it forms the background against which we work. At some point, all programmers — even object-oriented programmers — must lay out the data structures that their programs will use and define the functions that will act on the data.” (from The Objective-C Programming Language manual, © Apple Computer, Inc.)

“Object-oriented programming doesn’t so much dispute this view of the world as restructure it at a higher level. It groups operations and data into modular units called objects and lets you combine objects into structured networks to form a complete program. In an object-oriented programming language, objects and object interactions are the basic elements of design.”

The first idea of OO programming was due to Ole-Johan Dahl and Kirsten Nygaard (Simula-67); it was duly embraced by the industry only in the late 1980s, after a successful implementation in Smalltalk-80 (a language + run-time environment; Java has repeated the same approach 15 years later).

Objects

OO programming creates objects in an attempt to model real-world entities:

  • Tangible things (Students, Cars, Offices)
  • Roles (Voter, Electoral Office)
  • Incidents (Submission of Assignment, Act of Voting, Parachute Jump)
  • Interactions (Traffic Collision, Vote — relate Roles and Incidents)
  • Specification and Services (Set of Election Rules, Mathematical Rules etc)

object

Not every time an object can be constructed from the above list, but at least everything which goes as a noun or noun phrase can be considered to be modelled by an object. When deciding to model something by an object, one has to establish:

  1. Whether the potential object can have states?
  2. What, if any, behaviour the would be object can have?

By answering “yes” to both question, a virtual counterpart to the real one can be defined (whether it will be featured as an object in the final model, depends on design and other considerations, as we may see later in the course).

OO Paradigm vs Procedural Paradigm

Benefits of building your software system as a collection of objects include (by citing The Java Tutorial):

Yet the dynamics — the run-time properties — of an OO system can be much more complex than the dynamics of a procedural system. The relevant simile would be to compare an assembly line with the life cycle in a habitat populated by numerous species living and eating (incl. each other) side by side. Which way is better to build complex system? No simple answer is possible. And (some) software developers still question the overall benefit which OO has brought into the industry.

Classes

Software objects (well, most of them) in OO paradigm are virtual counterparts of objects in the real world. Just like the real-world objects of the same kind (Cars, Students, Homes,…) can be given a common, abstract, characterisation, so the OO programming objects of the same kind can be collectively described as examples of one type, which is formalised in a concept of class.

Philosophical Parallel

Syntax of Class Definition

Apart from primitive type variables, stuff which can live inside a Java program, can be of reference type. References are just names of the entities which have something inside them. Unlike primitive data types, these new entities are not “atomic”, they carry a number of constituents to hold data (which can be of primitive type, or be references themselves) and functions which may operate on those data, or on data which are passed as parameters to those functions. These complex entities are called objects. Primitive type variables are declared with their type, objects are declared with complex data type called classes. What lives inside objects and is defined to be the structure of classes are called class members, which in turn are divided into fields and methods.

[<modifier>]* class ClassName [extends ParentClass] [implements AnInterface]* {
   [<access-modifier>] [<scope-modifier>] returnType fieldName;
    ...  repeated as many times as there are fields ...

   [<access-modifier>] [<scope-modifier>] returType methodName([arguments]) {
       body-of-method definition;
   }

   [<access-modifier>] [<scope-modifier>] ClassName { 
     ... body of an inner class definition ...
   }
  ...  more methods and inner classes ...
}

Class definition

The class Planet, describing a celestial body — a planet, a satellite, an asteroid, a comet…

class Planet {
  public long idNum; public String name; // two declaration on one line, sorry!
  public Planet orbits; // not all fields are nouns
  public long id; // the catalog number of the new planet
  private static long nextID = 0; //the total planet counter

  public Planet(String s) { //one constructor
    this.name = s; 
    id = nextID++; // here the post-increment matter!
  } 

  public static long getNumberOfPlanets() { return nextID; }

  public Planet(String s, Planet p) {  //another constructor
    this.name = s; 
    this.orbits = p;
    id = nextID++;
  }
}
Planet moon; // variable declaration
Planet sun = new Planet("Sun"); // declaration and instantiation
Planet venus = new Planet("Venus", sun); // another instantiation 

Constructors and methods

A class can define several ways to create its objects: special “methods” (but not real methods), constructors, are called with operator new which allocates a memory for an object. A constructor looks like a class method without a return value and it requires the operator new. Multiple constructors in one class differ by type and number of parameters (and by exceptions they throw), but all have the same name as the class. When no constructors are defined, the default parameterless constructor is available (it’s inherited from parent class). If at least one constructor is defined, the default constructor is no longer available by default and must be defined explicitly (if needed).

class Student {
    String name; 
    Degree degree;
    ArrayList<Course> courses = new ArrayList<Course>(); 

    public Student(String n) {…}
    public Student(String n, Degree d) {…}

    public void enrollCourse(Course c) { courses.add(c); }
    public void study() {…}
    public Mark sitExam(Course c) {…}
     ... ...
}

Constructors and methods (concl.)

A defined class can be out to use to create and manage object of that class type:

Course comp6700 = new Course("comp6700"); // creating a new course
Degree bit = new Degree("MITS"); // new degree
Student stud = new Student("John Doe", bit); // start a degree
stud.enrollCourse(comp6700); // enrolling in a course
stud.study(); // "studying hard"
comp6700.setMark(stud,stud.sitExam(comp6700)); // earning a mark
...
stud.receiveGrades(comp6700);
...
stud.completeDegree();

“Student in memory”

It is useful to visualise an object memory layout:

stud_class_diag

Objects in memory: Fields and Methods

With every constructor call, a memory block structured in accordance with the class definition is allocated, as illustrated by the object diagram. The fields of a newly created object are initialised first to their default or initial values (the latter are the values which are assigned during the fields declaration, outside the constructors), and then the fields are assigned values as defined by the constructor.

The memory layout for (non-static) fields and methods is, actually different: the object methods aren’t grouped with the fields in memory (that would be wasteful). Memory is allocated for the instance fields of each new object, but there’s no need to allocate memory for methods. All an instance needs is access to its methods, and all instances of the same class share access to the same set of methods. There’s only one copy of the methods in memory, no matter how many instances of the class are created. However, whether access to the methods requires a reference to an object, or the methods can be called by the class name — this depends on the scope identifier in the methods declaration:

Member access

Fields and methods of a class (collectively known as members) are accessed by referencing the object name:

System.out.println("The name of this planet is " + sun.name);// non-static members

or by the class name:

System.out.println("The total number of planets is " + Planet.nextID); // static members

The access modifiers regulate how and which class member is visible — can be read, (re-)assigned or called (for methods):

  1. public — visible from anywhere outside
  2. "friend" (implicit, assumed if no access modifier is used; no such keyword) — visible within the same package
  3. protected — visible only to objects which are instances of subclasses of the class
  4. private — totally invisible from outside, can be only used inside the class; Different objects of the same class can access the other’s private members, see Shtuka.java

The general policy is to declare fields private and (non-auxiliary) methods public, but it really depends on design considerations. The values of a private field is read by calling a “getter” method, while its value is controllably changed with a “setter” method (not every class provides setter methods, immutable classes data, like in String, are not meant to be altered).

Member scope

Class members can have an object scope, or they can have a class scope, when they are declared static. Non-static members are associated with individual instances of the class (objects), and two values of the same field but from two different objects will have uncorrelated values:

public class Parameter {
   private int x; // declared static in StaticParameter class
   public Parameter(int x) { this.x = x; }

   public int getX() { return x; }
   public void setX(int y) { this.x = y; }

   public String toString() { return "" + x; }
}
Parameter p1 = new Parameter(1);
Parameter p2 = new Parameter(1);
System.out.println("p1 has " + p1.getX() + 
      " and p2 has " + p2.getX()); // prints "... 1 ... and ... 1 ..."
p1.setX(10);
System.out.println("p1 has " + p1.getX() + 
   " and p2 has " + p2.getX()); // prints "... 10 ... and ... 1 ..."

The modified Parameter (now StaticParameter) class will produce different output (study!).

Member variability

The nature of class members can be also controlled by (non-)presence of modifier **final**.

Normally, all modifiers which can be used in a method declaration, can be also used in a class declaration, with the similar meaning. So far unmentioned modifiers include:

Some modifiers cannot be used together, eg final and abstract together “do not compute”.

Method overloading

Taste of OO power

The method println(), which we have used multiple times already, seems was one and the same despite the arguments were different (values of all types which we wanted to be printed). The sameness of the println() was an illusion — those were, in fact, different methods! Different by the type (and number) of parameter which one can pass to them. But they all can have the same name! A method whose name used in other methods which differ by the type and the number of their parameters is called overloaded. Below is the code from The Java Tutorial:

public class DataArtist {
    ...
    public void draw(String s) { ... }

    public void draw(int i) { ... }

    public void draw(double f) { ... }

}

But it’s illegal to declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart. Since the compiler does not consider return type when differentiating methods, you cannot declare two methods with the same parameter set if they have a different return type. General advice — not overuse the overloading, since it hinders the code readability. Especially, do not overload methods with the same number (but different type) of parameters, including varags.

Constructors, defaults, this and all that

The constructor definitions can contain any code, but their primary purpose is to initialise the class fields. Fields which are not initialised by the constructor explicitly, are given default values:

static fields can sometimes be assigned in the constructor:

public Planet(String s, Planet p) {
    this.name = s; // the self-reference this is optional
    this.orbits = p; // it's a good practice to avoid errors
    idNum = nextID++; // counting number of created objects
}

this is used for object self-referencing (eg, if it is passed as a parameter).

public ArrayList<Planet> satellites; // ArrayList is like an array
public Planet(String s, Planet p) {
    this.name = s; 
    this.orbits = p;
    p.sattellites.add(this);
}       

Object creation

Three stages — when class is loaded, then when its constructor is called but before it’s executed, and finally after the constructor executed:

The class is loaded before after
public class Dummy {
   int i;
   String str;
   Dummy(String s) {
      str = new String(s);
   }
}

dummy_one

dummy_two

Notice the appearance of an additional String object at the second stage due to presence of new operator inside the Dummy constructor which results in a creation of new String object. If the assignment of str value was different: str = "abcdefg"; — then no new String object was created, but the reference str was given a value of the reference to the constant string “abcdefg” (remember String’s pool).

Inheritance: code re-use

The ability to re-use existing code (in the form of class definitions), or inherit previous definitions in Java is realised via the mechanism of inheritance. A new subclass (child, derived) can be defined to inherit the definition of an existing class (superclass, parent) and also to add new class members and change the inherited ones.

class A {                                             
   protected int oldField;                            
   public void oldMethod();                           
}                                                     
class B extends A {                                   
   ... A's public/protected members are included      
   ... their access modifiers can be only changed to  
   ... raise their visibility, eg protected -> public 
   public B(..) { // this is B's constructor          
      super(..); // call to parent constructor        
      newField = some_value; }                        
   private int newField;                              
   public void newMethod();                           
}                                                     

Everything (fields, methods, constructors) from the parent class can be accessed in the child class using the parent reference super (an example of constructor’s re-use is Manager.java. Call to super() is always a good practice in every child constructor.

A PIE of OO

The Object-Orientation paradigm is like a pie (using Peter van der Linden’s simile):

a_pie

The most important aspects of the OO paradigm:

Inheritance: Constructor’s work

What happens when the constructor is called?

When an object is created, memory is allocated for all its fields, including those which are inherited from superclasses (parent and all the ancestors all the way up to the Object class — Abraham of Java’s class hierarchy). The class fields are set to their respective default values before the constructor’s phases begin:

  1. The superclass constructor — either default super() or one with parameters super(...) — is invoked;

    • phases 1,2,3 of the previous level are executed based on the code of the superclass;
  2. The class fields are initialised using their initialisers and initialisation blocks (the blocks of code outside the constrictors or class methods which initialise the fields of the object; see the example Body.java) in the same order in which they are declared in the class

  3. The rest of the constructor body — everything after super() — is executed (that’s why super() should be the first statement inside the constructor body, or be absent altogether);

Explicit constructor invocation: One class constructor can invoke another constructor of the same class by calling this(...). super(), or this() must be the first constructor statement. this() helps to write a reusable constructor code only once, and then invoke it by calling this(arg1,arg2,…) in other constructors when same initialisation is required.

Important: if a class can be extended, do not call any non-final non-private methods inside its constructor!

Constructor Phases

This table shows the values which the Y subclass (of the class X) fields have at different stages of the Y() constructor executions. The source code for the parent class X.java and its child Y.java is good for understanding what is gong on:

Step What Happens xMask yMask fullMask
0 Fields set to defaults 0 0 0
1 Y constructor invoked 0 0 0
2 X constructor invoked 0 0 0
3 Object constr. invoked 0 0 0
4 X field initialised 0x00ff 0 0
5 X constructor executed 0x00ff 0 0x00ff
6 Y field initialised 0x00ff 0xff00 0x00ff
7 Y constructor executed 0x00ff 0xff00 0xffff

Inheritance Hierarchy

class Person { void justLive(); }
class Student extends Person {
    void study();
}
class Undergraduate extends Student {
    void study() { /* just study */ };
}
class ResearchStudent extends Student {
    void study() { /* do research */ }
}
class Employee extends Person {
    void work();
}

hierarchy

In an inheritance hierarchy, every class from a sub-tree represents a subtype of the type represented by the root of sub-tree. Type and its subtype are in “IsA” relationship.

Person p = new Student("Jack Sparrow"); // upcasting, OK: Student IsA Person
Student st = new Person("Bill Turner");  // illegal, class cast exception
Student st = p; // unsafe, p may not be Student type
p.study(); // illegal, downcasting, needs explicit cast
((Student)p).study(); // OK if p IsA Student, or class cast exception is thrown 
Employee e = new Student("Barbossa"); // illegal, incompatible types

Inheritance: Overriding

Every class in Java inherits, either explicitly via extends, or implicitly from the Object class. Hence, all public and protected methods from the Object class are available to all instances and classes which can be defined and used in any Java program. Two such methods, equals() and hashCode(), and how and why they should be overridden will be discussed later. The inheritance does not only allow to re-use existing code. It also let you modify an inherited definition to meet new requirements. This is achieved through overriding of inherited methods — providing a new implementation without changing the signature and the contract (to achieve the polymorphism, see next slide) of the inherited method.

class Employee {
    ...
    double pay() {
       return salary;
    }
}
class Manager
  extends Employee {
      ...
    double pay() {
      return super.pay()
          + bonus;
    }
}

subclass

Parent’s attributes are accessed via super reference anywhere inside the child class. Multiple parent constructors differ by their parameters, as ordinary methods. Eg, the Manager constructor which calls super(String,String,double) refers to Employee(String,String,double).

Polymorphism and dynamic binding

The mechanism of extending an old type isn’t just good in terms of code re-use. It allows to write shorter, yet more flexible and expressive programs. Namely, if the client of your code can use an object of some class, it can also use objects of its subclasses. This feature is called polymorphism, meaning that an object of a given class can, in fact, have multiple forms — of its own class or of any class it extends. Put it another way: polymorphism is the ability for different objects to respond to identical messages.

All fields and methods of a given class, which are visible outside (1), together with the conditions when they are used (2) and the effects they produced (3), are collectively known as the class contract — it represents the declared raison d’etre of the class. Class extension provides two kinds of inheritance:

Example of the payroll() method in the EmployeeTest.java program. Depending on the class of the Employee object which is used in the payroll() method, different getSalary() methods are used. This binding of the call (ie, method invocation) to a particular method is known as dynamic binding. Another set of examples are shapes and their area.

Covariant overriding

A method can also return a reference type. For instance, a method for calculating a Number type value which is what the method returns:

// in class Number
public Number calculateTheNumber(...) {
   Number n;
   .... .... ....
   return n;
}
// in class ComplexNumber
public Number calculateTheNumber(...) {
   Number cn; // replace by the next line
   //ComplexNumber cn;
   .... new implementation ....
   return cn;
}

covar_over

If the class Number is extended to ComplexNumber and you want to override calculateTheNumber() to return, quite appropriately, the ComplexNumber value, this is possible. This technique, called covariant return type, means that when overriding, the return type is allowed to vary in the same direction as the subclass (see covariant overriding example).

Abstract classes: Partial implementation

Abstract classes allow to capture an abstraction — a state or behavioural feature which is common in a family of types, which they all have, but in somewhat different (particular) way. The whole family is characterised by declaring that something is done, but not how it is done. The “how”, ie, the concrete implementation, differs from one type to another. In Java, an abstract class usually has one or more declared, but not implemented methods which are marked with the keyword abstract (one can declare a class abstract without having a single abstract method in it). Abstract classes cannot be instantiated. The classes which provide implementation of abstract methods (concrete subclasses) must extend the abstract class. The opposite is also possible: a class can extend a concrete superclass and “override” a normal (implemented) method to make it abstract (why would it be useful?).

public abstract class Person {
   ... class fields as in an ordinary class
  public abstract void doSmth();
   ... plus more abstract and/or normal methods
}

public class Student extends Person {
    public void doSmth() {
       study();
       studyMore();
       partyAtLast();
    }
}

abstract

Multiple Inheritance

Multiple inheritance — an ability to define a type which combine properties of more than one existing is a double-edge sword: powerful and dangerous.

It would be often beneficial for a new class to inherit at once from more than one parent, eg, a HollywoodActor object can go on trial for a (real life) crime, like tax evasion or murder. In such case, a class AccusedActor can be created which needs to inherit from both the HollywoodActor and Civilian (to account for attributes like SS number, or payTax() method). (Less queer example is the SwissArmyKnife class.)

The diamond of death problem

The problem with multiple inheritance occur when two (or more) parents of the same class have common ancestry. Who is super? ClassB or ClassC? If each of them had modified doSmth(), or had hidden aField, which copy of them ClassD inherited?

Some OO languages (C++, Eiffel) allow multiple inheritance, but resolving DDP is complex and makes design and use of class hierarchy more complicated. Java constrains the multiple inheritance by allowing only single inheritance of implementation and multiple inheritance of contract.

multiple

Interfaces: Only declared behaviour

The way around the multiple inheritance problems in Java is to introduce interface. It can be thought as the abstract class stripped of any implementation and variable fields. No implementation! Pure contract. The only members allowed are non-private method declarations (no need to call them abstract), and constants —— static (no instantiation!) and final (not blank! with assigned constant values) fields — public static final modifiers can be omitted in declarations. The interface declaration is marked by interface keyword instead of class:

interface Verbose {
   int SILENT  = 0; // interface constants have fully CAPITALISED identifiers
   int TERSE   = 1;
   int NORMAL  = 2;
   int VERBOSE = 3;

   void setVerbosity(int level);
   int getVerbosity();
}

In the manner of extending abstract class to get complete implementation and object creation, an interface must be implemented into the concrete class via implementation of every method declared in the interface.

class MyConcreteClass implements Verbose { .... }

Interfaces: Implementation

The diamond of death phenomenon cannot occur if the implementing class inherits only one (abstract or concrete, does not matter) class, and implements a number (I couldn’t find the max number, even in The Java Language Specification book, but I guess it’s 256; imaging a class which implements that many interfaces!) of interfaces, because there is no competing implementations which vie to become the one in the derived class (there is at most one).

If a class implements multiple interfaces, they are declared in a comma separated list following the implements keyword:

class ClassD extends ClassA implements InterfaceB, interfaceC {
   ... adding own fields and methods ...
   ... possibly overriding other methods form ClassA ...
   void doSmth(){
   ... providing unique implementation (perhaps overriding) ...
   }
}

Interfaces as types, polymorphism again

Interfaces allow to decouple the behaviour which classes from one group have (because they implement a particular interface) from classes of another group which use this behaviour (they are provided with an interface to the behaviour).

Interfaces can be extended (NewInterface extends OldInterface) to add new method declarations to the interface type without breaking existing classes which implement the OldInterface type. Unlike class extension, the interface can extend multiple interfaces (no implementation is provided, and declarations do not clash; the parent interface constants get hidden).

Abstract Classes vs Interfaces

Which one to choose

Repeat the difference:

Therefore:

Deeper view of Object

The idea that object encapsulates its implementation and reveals its interface represents only the technical aspect. A deeper and more powerful idea is that Object is its Interface.

If an object is an idea of computation, then it is more stable and has more longevity if it has less accidental (implementation specific) characteristics. In its ideal, purified form an object is defined by operations which one can perform with it, and by nothing else.

The watch measures time, it does not count it!

interface
(Courtesy of Apple Inc.)

Three kinds of type in Java

Three reference types — classes, abstract classes, and interfaces — seems excessive, isn’t?

  1. Concrete classes — No questions! Templates for real objects
  2. Abstract classes — Understandable! Help to reuse code
  3. Interfaces — ???

The most important OO idea: “Program to interface!”

When writing a program, try — as much as possible — to program to interface. The client code of an object (a class which has it as a field, or a method which uses it as an actual parameter, or a code block which is given access to the object) should be only revealed that part of its interface which they actually use, not more! If the object has many types (ie, it instantiated to a class which implements multiple interfaces), and the client needs only one of them, it is only with that type the object should be declared to its client.

Physics analogy: motion of a bunch of electrically charged particles (electrons) in an external electro-magnetic field. Electron’s internal structure needs not to be known to calculate the trajectories; only their “interface”: charge, mass, position and velocity. Future advances in Physics may reveal electron internal structure, but the laws of motion will remain intact.

Ideas, concepts and knowledge in general (including software) — they are most useful, stable and valuable if they are expressed in abstract terms.(How many ideas and how many functioning artefacts from Antiquity are around today?) In software, this abstraction of object properties is their interface.

Interface View of the World

What do we need to know to fully describe the motion of charged particles in external electro-magnetic field? Only charges and masses, not the inner structure.

moving_charges

Dynamics in terms of “Interfaces”

Signals and methods in OO programs

  1. (time t1, t2…): Objects o1o6 are created at various stages of execution

  2. (t2): An object o1 sends a signal to o4 with data containing a reference to an object o2

  3. (t3 >= t2): The object o4 receives this signal and executes a method (or, several methods); the received signal contains a reference to o2 (a parameter to one of o4 methods), and o4 may invoke methods of o2 as a part of its response; the state of o4 may change, and it may emit a signal to another object o3 passing some data; the data which are passed are obtained as return values of methods which the object o4 executed between receiving the signal from o1 and sending the signal to o3

  4. (t5): All references to the object o2 are lost, this object “dies” (is garbage-collected)

  5. States of all objects undergo evolution during the program execution (red lines), which depends both on the global program logic (“external force”), and on signals they receive from other objects (including their own).

Design for inheritance

When a class is extended and an implementation of its method is changed, it’s said then the method is overridden (not confuse with method overloading). When we are dealing with instances of a class, it is the actual class of the object which determines which implementation is used. Apart from changing implementation, the overriding can widen the access modifier, ie, make protected method public, but it cannot make it private. If the parental implementation need to be used, it can be done by using the reference to the superclass: super.do().

When designing a class for extension, one must consider which access modifier to give to fields (private or protected, the last choice can be good for performance but must be exercises carefully) and to methods (protected or public). As every design aspect, this is a complex issue and requires experience and care, but the rule of thumb is that if you do not trust the class’s possible children to preserve the integrity of the class contract, you should limit the access of its members. If the class method should not be overridden no matter what, it must be declared final (this will disallow any possible future overriding of the method by its descendants). If the whole class is not meant to be extended, it itself must be declared final (all methods in such a class are implicitly final).

final class NonExtendableClass { .... }

Attempts to declare a class which extends NonExtendableClass will be met with obstinate compiler error.

Overriding and hiding

One tricky issue

If the inherited instance methods can be overridden, the inherited fields (if the child class introduces a field with the name but not type identical to a field in the parent class) and static methods are hidden. For a field in the subclass with the same name as a field in the superclass, the latter still exists, but it’s no longer accessible by its simple name. The reference must be cast to the superclass type to access it.

class SuperShow {
    public String str = "SuperString";
    public void show() { System.out.println("Super.show: " + str);}
}
class ExtendShow extends SuperShow {
    public String str = "ExtendString";// hiding the field
    public void show() { // overriding the method
        System.out.println("Extend.show: " + str); 
    }
}

Run InheritanceTest class (which involves the parent-child pair SuperShow and ExtendShow):

Extend.show: ExtendString // method is selected by the object class
Extend.show: ExtendString
sup.str = SuperString // field is selected by the reference type
ext.str = ExtendString

Class method hiding

Class (static) methods behave similarly to fields

Some overriding does not make sense: overriding a class method into instance method (stripping static) doesn’t make sense, and vice-versa — overriding an instance method into a static one. Both attempts result in the compile errors.

Defining a Method with the Same Signature as a Superclass’s Method

Kind of Inheritance Superclass Instance Method Superclass Static Method
Subclass Instance Method Overrides Illegal (Compile Error)
Subclass Static Method Illegal (Compile Error) Hides

For examples, see overrideStatic three class code.

Note: In a subclass, you can overload methods inherited from the superclass. Such overloaded methods neither hide nor override the superclass methods—they are new methods, unique to the subclass.

Note: When overriding a method, you might want to use the @Override annotation that instructs the compiler that you intend to override a method from the superclass. If, for some reason, the compiler detects that the method does not exist in one of the superclasses, it will generate an error.

Reference type, actual class and super

The super can be invoked in any non-static methods. It acts as a reference to the current object as an instance of its superclass. When you need to select a parental implementation even if the reference is attached to an instance of the child class, use super.

class That {
    protected String getName() { return "That"; } //return the class name
}
class More extends That {
    protected String getName() { return "More"; } //overrides the superclass method
    void printName() {
        That sref = (That) this; // no need to do the cast, though
        System.out.println("1   this.getName() = " + this.getName());  
        System.out.println("2   sref.getName() = " + sref.getName());  
        System.out.println("3   super.getName() = " + super.getName());
    }
    public static void main(String[] args) {
        (new More()).printName(); }
}

Both sref and super refer to the same object of the type That, but super will ignore the real class of the object and use the superclass implementation.

1   this.getName() = More
2   sref.getName() = More
3   super.getName() = That

Equality of references and objects

When two objects of the same class can considered equal but independent? This depends on how we define the object equality.

two_dummies

dummy_equal

dummy_clone

Dummy d1, d2;
d1 = new Dummy("D1");
d2 = new Dummy("D2");
(d1 != d2) &&
(!d1.equals(d2))
d2 = d1;
(d1 == d2) &&
(d1.equals(d2))
d2 = d1.clone();
(d1 != d2)
   but is
d1.equals(d2) ?

equals()

Objects of the same type are often compared on equality with one another. The method Object.equals(Object o) returns true only if the objects are one and the same (the default implementation is the test o == this). Some classes do require this kind of behaviour (like Thread, which represents a process, not a value). But often equals() is required as the test of logical equality, when two instances of a value class are considered equal not only when they not refer to the same object, but also when the objects can be substituted for one another without altering the computational environment. Such equals() methods are important for search and placement of elements in instances of Collection classes. Demo with two versions of equals() in the A class (the test running program is TestingEquals.java).

To work correctly, the overridden equals() must satisfy the equivalence relations:

Problems and solutions with equals()

Overriding equals() can occur in two ways:

The above equivalence relations cannot be satisfied all at once if is is done on the way of inheritance — there is simply no way to extend a class and add an aspect (a new field) while preserving the equals() contract" (for proof see Joshua Bloch’s book “The Effective Java”).

However, equals() can be defined with the above properties on the way of composition.

class ColourPoint { 
    Point point;
    Colour colour;
    public boolean equals(Object o) {
        if (!(o instanceof ColourPoint)) return false; 
        ColourPoint cp = (ColourPoint) o; 
        return cp.point.equals(point) && cp.colour.equals(colour);
    }
}

One case, when there is no need to override equals, is when the a class is defined in such a way that at most one object of it can be instantiated (Singleton pattern). Another example of types for which equals() is equivalent to == is Enum (they allow only a finite number of instances which are defined as a part of the enum type declaration).

clone()

Sometimes, the client code needs to create a copy of an object which has the same state as the prototype object. This procedure is called cloning. The method which can do such creation, clone(), is defined in the Object class; it is a native method. The Object.clone() method returns a reference to the Object type object which must be appropriately cast. However, the returned object must be otherwise independent from the original one such that subsequent changes to the newly cloned object do not affect the original object (deep clone). This task cannot be achieved by simply calling the inherited clone() — the Object.clone() is declared protected, and every subclass needs to explicitly override it, and either keep it protected, or promote it to public (not always a good idea). When overriding the clone() method in a derived class, one should:

Good clone()

An example, given in B.java class, is demonstrated with both — naive (incorrect) and correct versions of clone(). Class B uses a private buffer field (a simple array of int) to provide a stack type data structure (for details, see Block-4) which allows to push() a value into the stack, pop() the latest added vale, and read the latest added value with getTop(). What if we attempt to clone an existing stack object of B which then could be used as independent stack? A very important aspect of cloning is to make sure that buffer is correctly cloned too:

public B clone() {
  try {
    // recreating the old object with shared reference fields
    B tmp = (B) super.clone(); 
    // calling the corresponding field's clone
    tmp.buffer = buffer.clone(); /* omit this and you're in trouble! */
    return tmp; // provided buffer.clone() is already correct
  } catch (CloneNotSupportedException e) { 
     // Cannot happen -- 'cause we supported the clone
      throw new InternalError(e.toString());
  }
}

Shallow and deep clone

The cloning problem is a delicate one and it is dealt with differently by languages:

shallow_clone deep_clone

Implementing clone() is a messy business (in Java). Often, a much better way to program object creation in a given state is to define a copy constructor; this provides a simpler alternative (eg, it can deal with final fields).

Turning Java into a pure OO language: Wrapper classes

Presence of primitive data types in Java was a performance “hack”, rather then a necessary feature (Smalltalk, or Eiffel which predated Java were pure OO, without primitives): primitive type variables do not incur initialisation overhead compared to objects. Also a factor was to maintain the type system familiar by making it similar to C/C++. The trade-off was to sacrifice the expressiveness and uniformity of type system. This artificial division between two kinds of type was not just illogical, but resulted in practical limitation (eg, some collection types, like the Vector class, can be only “stuffed” with objects). To breach this logical divide, Java provided wrapper classes for each primitive type: Boolean, Character and the abstract Number (with concrete subclasses to represent the number types.)

This classes can be instantiated to carry the data which the corresponding primitive type do. They also provide additional services, like conversion, parsing values, etc.

int i = 10;
Integer j = new Integer(11); // wrapping a primitive value
i = j.intValue(); // getting it back from an object
double k = j.doubleValue();
Integer l = Integer.decode("0xAAA"); //decodes string representation of hex number
i = Integer.parseInt((new Scanner(System.in)).next());

Purists can argue that coexistence of reference and non-reference types is a flaw in language design (“considered harmful” by Nick Ourusoff, Comm. ACM 45 (8) 2002), because the expression evaluation for primitive types breaks the OO paradigm, data representation is confused with object encapsulation, the machine domain is confused with the application domain.

Auto Boxing/Unboxing

Prior to Java 1.5 there was awkward practical aspect to existence of primitives, too. If numbers object (instance of ArrayList<Integer>) is used to store values, every element in it is an Integer object, and the extracted element must be converted if to be assigned to an int variable:

int i = 10;
numbers.add(new Integer(i));
Integer j = numbers.get(4); // getting the copy of the element at index 4
int k = j.intValue();

Java 1.5 has made this explicit conversion unnecessary:

Integer val = 3; // in-boxing conversion
int i = numbers.get(4); // un-boxing conversion

The class Freq created the word-frequency map reading from the command line:

public class Freq {
   public static void main(String[] args) { 
      Map m = new TreeMap();
      for (String word : args) {
         Integer freq = m.get(word);
         m.put(word, (freq == null ? 1 : freq + 1)); 
      }
   System.out.println(m); 
   }
}

Object-Oriented Glossary

Basic OO terms and concepts

Principles of class design

OO without classes — Prototype Programming

What is more important for OOP — classes or objects? A class is an accumulation of properties which are common to instances. When a computation is conceived “from the top”, defining classes is an adequate approach to capture abstraction, and class based implementation for creating objects is appropriate. Since Java is a statically typed compiled language, once an object is created, its structure and behaviour will not change (unless you are using the reflections or some other “black art”, that is). This is good since it guarantees that object’s contract will not change (which gives a layer of security to be expected in statically-typed languages), but sometimes one can benefit from the ability to change object’s properties while it is alive. Dynamic OO languages (like Python) allow just that, but they still begin with classes (in Python, there are ways to restrain object’s variability via the slot-mechanism).

What if the problem in question does not yield to easy classification: multiple objects do exist, but finding common features to define their class is not possible. (In philosophy, this problem was emphasised by Ludwig Wittgenstein). There is an alternative paradigm which is known as prototype based (OO) programming.

In languages like Self, Lua, Smalltalk (which allows classes, too) and (most popular) JavaScript, objects are created by cloning from a set of predefined object literals which are called prototypes, with the ability to add new attributes and behaviour during the object lifetime. In the prototype-based languages, the notion of type hierarchy does not exist.