Basics of Object-Oriented Programming

This handout has moved here.

The topics discussed in this handout are considered the basics of object-oriented programming. Some of them are language-independent, others are Java- or C#-specific. These topics, except perhaps for the last two, are supposed to be covered by the prerequisite chain for this course.

Reference semantics vs. value semantics

Java · C# (Effective C# item 6)
  • Value semantics: variables directly contain values.
  • Reference semantics: variables contain addresses that refer (point) to objects.


Equality vs. identity

Example · Java (Effective Java items 8, 9; see also here) · C# (Effective C# items 6, 26)
  • Equality: are two (possibly) different objects equivalent?
  • Identity: do two references refer to the same objects?
  • How are equality and identity related?
  • Reconciling value and reference semantics: identity of objects explained as equality of addresses.

Parametric Polymorphism (Generics)

Java (Effective Java chapter 5: items 23-29; see also here for advanced issues· C#

Familiar from data structures course:
  • without generics: Stack (of objects); loose typing
  • without generics: IntStack, StringStack, BookStack; strict typing but lots of duplicate boilerplate code
  • with generics: Stack<Int>, Stack<String>, Stack<Book>; strict typing without code duplication
Relatively easy to use, can be challenging to use correctly in the design of one's abstractions.

Relationships among classes and interfaces

These are common relationships among concepts and are part of UML's class diagrams.

Class/interface-level relationships

These relationships are between classes and/or interfaces, so they cannot change at run time.

From strong to weak:
  • is-a, realizes-a: generalization/specialization (subtyping)
  • uses-a: dependency

Instance-level relationships

These relationships are between instances, so they can change at run time.

From strong to weak:
  • owns-a: composition
  • part-of: aggregation
  • has-a: association

Class-interface continuum

Java (Effective Java items 18, 19) · C# (Effective C# items 22, 23)
  • Concrete class: can be instantiated. All specified methods are fully implemented.
  • Abstract class: cannot be instantiated. Some or all of the specified methods are not implemented.
  • Interface: limit case of a fully abstract class for specification purposes only. None of the specified methods are implemented, and there are no instance variables.
Related to the single-responsibility and interface-segregation principles.


Subtyping vs. subclassing/inheritance

Java (Effective Java item 17; see also this tutorial and these these pitfalls) · C# (Effective C# item 22)
  • Subtyping allows substituting a more specific object for a more general one, for example, when passed as an argument or assigned to a variable.
  • Inheritance is a mechanism for a subclass to reuse state and behavior from a superclass.
    • inherit methods and fields
    • add fields
    • add or replace/refine methods
  • Inheriting from a superclass enables weak syntactic subtyping. (In some languages, this relationship can be public or nonpublic.)
  • The Liskov Substitution Principle (LSP) defines strong semantic (behavioral) subtyping.
  • Implementing or extending an interface also enables syntactic subtyping (and semantic subtyping because interfaces have no behavior).


Subtype Polymorphism: Static vs. dynamic type

Java (Effective Java item 52) · C# (Effective C# item 3)
  • Static type: declared type of a variable.
  • Dynamic type: actual type of the object to which the variable refers.
  • Dynamic method binding: x.f(a1, a2, ...). Two steps
    1. Verify whether receiver x supports method f based on static type.
    2. Search for version of f to be invoked starting from dynamic type and proceeding upward until found.
  • How are static and dynamic type of a variable related?
  • If step 1 succeeds, will step 2 always succeed as well?
  • Casting: treat an object as if it had a different static type. Three different situations: downcast, upcast, crosscast.
  • Overloading versus overriding.
    • @Override correctness in Java

Being a good descendant of java.lang.Object/System.Object 

Java (Effective Java items 8 through 12; see also here for equals, below for clone; detailed Javadoc is here) · C# (Effective C# items 5, 9, 10, 27)

Classes are usually required to provide the following methods:
  • toString (for displaying instances in a meaningful way)
  • equals (if an instance can be in an equivalence class that include other instances)
  • hashCode (ditto)
  • compareTo (if instances are ordered)
  • clone (if instances are mutable)
  • finalize (if instances need to release resources)
Also related to the Liskov substitution principle.

Clone in the context of the Composite pattern

Java (Effective Java item 11; see also here for more detail) · C# (Effective C# items 14, 27)

In general, cloning allows you to make a copy of an object. The clone method in Java is similar to the copy constructor in C++, but it is an ordinary method, unlike the copy constructor. Once you have the original object and its clone, then you can modify each one independently. Accordingly, cloning is necessary only if the objects are mutable.

Cloning models the real-life situation where you build a prototype of something, say a car or a piece of furniture, and once you like it, you clone it as many times as you want. These things are composites, and the need to be cloned deeply (recursively).

As another example, imagine a parking garage with a list of cars that have access to it. To build another garage to handle the growing demand, you can clone the garage and the customer access list. But the cars should not get cloned. That's because the garage is not composed of the cars.

As we can see, the conceptual distinction between aggregation and composition has significant consequences for the implementation of the relationship. True, both relationships are represented as references in Java. However, composites usually require a deep clone (if cloning is supported) where each parent is responsible for cloning its own state and recursively cloning its children.

As mentioned above, you don't need to support cloning at all if your objects are immutable because you wouldn't be able to distinguish the original from the clone anyway.


Java (see also here) · C#
  • Mechanism for grouping related or collaborating classes (cf. default package-level member access).
  • Implemented as mapping from fully qualified class names to file system.


Member access

Java (Effective Java items 13, 14, 15; see also here) · C# (item 1)
  • public
  • protected
  • default (package)
  • private
Related to the information hiding and open-closed principles.