Skip to main content

Section 8.4 Classes

In larger programs, we very commonly work with compound data types (called struct in C). A compound data type is the cartesian product of several component types. A compound data type's value is therefore a tuple of several component values. Consider the following struct in C, which is intended to represent a point in the two-dimensional plane:

typedef struct {
  double x, y;
} vec2_t;

Since modifications and computations on structs usually involve more than one struct field, it is common to implement them in functions:

void vec2_translate(vec2_t *p, double dx, double dy) {
  p->x += dx;
  p->y += dy;
}

void vec2_length(vec2_t const *p) {
  return sqrt(p->x * p->x + p->y * p->y);
}

These functions share a common feature: Their first parameter is a pointer to a struct (i.e. the value of a compound data type). In object-oriented programming languages like Java, such functions are directly attached to the compound data type. In Java, we would write:

class Vec2 {
  double x, y;

  void translate(double dx, double dy) {
    this.x += dx;
    this.y += dy;
  }

  double length() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  }
}

The object-oriented programming field has its own nomenclature: Compound data types are called classes and a value of a class is called an object or an instance. Functions that are connected to classes are methods and, as we have previously seen, pointers are called references. Every method has an implicit parameter this: It is automatically declared by the compiler and does not need to be declared by the programmer. this is a reference to an object of the class in which the method is declared. In the above example, the type of this is therefore Vec2. If identifiers that are declared in the class are not overshadowed by local variables or parameters of the method, the qualification this. can be omitted. This means that we can also define the length method of Vec2 also as follows:

class Vec2 {
  ...
  double length() {
    return Math.sqrt(x * x + y * y);
  }
}

Subsection 8.4.1 Constructors

A class needs to provide one or more constructors to initialize freshly created instances of the class. A constructor has the same name as its class and no return type. For our example, it is sensible to add a constructor that initializes a Vec2 object with an \(x\)- and a \(y\)-coordinate:

class Vec2 {
  double x, y;

  public Vec2(double x, double y) {
    this.x = x;
    this.y = y;
  }
  ...
}

All member fields of an object that are not explicitly initialized by the constructor are initialized with their default value in Java (see Table 8.2.1).

A special case is the default constructor, which is implicitly declared in Java if no other constructor is declared in a class. It behaves as the following declaration:

public class Vec2 {
  public Vec2() {
  }
}

We can create new objects with new and one of the constructors of the class:

public class Test {
  public static void main(String[] args) {
    Vec2 a = new Vec2(1.0, 0.0);
    System.out.println(a.length());
  }
}

The value of the expression new Vec2(1.0, 0.0) has the type Vec2 and is a reference to a newly created object of the class Vec2. Like in C, the lifetime of the container associated with the variable a is limited to the surrounding block. The referenced object however stays alive as long as the program state contains references to it. Once no living container contains a reference to the object anymore, it is deallocated by the garbage collection.

To call a method, the reference of the corresponding object does not occur explicitly in the parameter list, but it is prepended as qualification to the method name with a separating dot. When the call is executed, this reference is copied to the implicitly declared this container of the method.

Example 8.4.1.

Consider the call a.length() in the above example. Although no expression is in the parentheses, the method length receives an argument: the value of the expression a (a reference to an object). Compare this to the C version vec2_length of this method, which explicitly takes a parameter.

Remark 8.4.2.

The reason for this special notation is that this first parameter plays a special role for choosing the method that is called, as we will see in Section 8.7.