close this bookHow to Think Like a Computer Scientist
source ref: ebookit.html
View the documentMetadata
View the documentChapter 1:The way of the program.
View the documentchapter 2:Variables and types
View the documentchapter 3:Methods
View the documentChapter 4:Conditionals, graphics, and recursion
View the documentChapter 5:Methods that return things
View the documentChapter 6:Iteration
View the documentChapter 7:Strings and things
View the documentChapter 8:Interesting objects
View the documentChapter 9:Create your own objects
View the documentChapter 10:Arrays
View the documentChapter 11:Object methods
View the documentChapter 12:Arrays of Objects
View the documentChapter 13:Object-oriented programming

Expanding the text here will generate a large amount of data for your browser to display

Chapter 11:Object methods


Chapter 11

Object methods



11.1 Object and class methods

There are two types of methods in Java, called class methods and object methods. So far, every method we have written has been a class method. Class methods are identified by the keyword static in the first line. Any method that does not have the keyword static is an object method.

Although we have not written any object methods, we have invoked some. Whenever we have invoked a method "on" an object, it has been an object method. For example, drawOval is an object method we invoked on g. Also, the methods we invoked on Strings in Chapter 7.1 were object methods.

Anything that can be written as a class method can also be written as an object method, and vice versa. Sometimes it is just more natural to use one or the other. For reasons that will be clear soon, object methods are often shorter than the corresponding class methods.



11.2 The current object

When you invoke a method on an object, that object becomes the current object. Inside the method, you can refer to the instance variables of the current object by name, without having to specify the name of the object.

Also, you can refer to the current object using the keyword this. We have already seen this used in constructors. In fact, you can think of constructors as being a special kind of object method.



11.3 Complex numbers

As a running example for the rest of this chapter we will consider a class definition for complex numbers. Complex numbers are useful for many branches of mathematics and engineering, and many computations are performed using complex arithmetic. A complex number is the sum of a real part and an imaginary part, and is usually written in the form x + iy, where x is the real part, y is the imaginary part, and i represents the square root of -1. Thus, i * i = -1.

The following is a class definition for a new object type called Complex:


public class Complex
{
  // instance variables
  double real, imag;

  // constructor
  public Complex () {
    real = 0.0;  imag = 0.0;
  }
   
  // constructor
  public Complex (double real, double imag) {
    this.real = real;  this.imag = imag;
  }
}

There should be nothing surprising here. The instance variables are two doubles that contain the real and imaginary parts. The two constructors are the usual kind: one takes no parameters and assigns default values to the instance variables, the other takes parameters that are identical to the instance variables.

As usual, we have the option of creating the object and then setting the instance variables, or doing both at the same time:


    Complex x = new Complex ();
    x.real = 1.0;
    x.imag = 2.0;
    Complex y = new Complex (3.0, 4.0);



11.4 A function on Complex number

Let's look at some of the operations we might want to perform on complex numbers. The absolute value of a complex number is defined to be sqrt(x2 + y2). The abs method is a pure function that computes the absolute value. Written as a class method, it looks like this:


  // class method
  public static double abs (Complex c) {
    return Math.sqrt (c.real * c.real + c.imag * c.imag);
  }

This version of abs calculates the absolute value of c, the Complex object it receives as a parameter. The next version of abs is an object method; it calculates the absolute value of the current object (the object the method was invoked on). Thus, it does not receive any parameters:


  // object method
  public double abs () {
    return Math.sqrt (real*real + imag*imag);
  }

I removed the keyword static to indicate that this is an object method. Also, I eliminated the unnecessary parameter. Then I can refer to the instance variables real and imag by name without having to specify an object. Java knows implicitly that I am referring to the instance variables of the current object. If I wanted to make it explicit, I could have used the keyword this:


  // object method
  public double abs () {
    return Math.sqrt (this.real * this.real + this.imag * this.imag);
  }

But that would be longer and not really any clearer. To invoke this method, we invoke it on an object, for example x.abs().



11.5 Another function on Complex numbers

Another operation we might want to perform on complex numbers is addition. You can add complex numbers by adding the real parts and adding the imaginary parts. Written as a class method, that looks like:


  public static Complex add (Complex a, Complex b) {
    return new Complex (a.real + b.real, a.imag + b.imag);
  }

To invoke this method, we would pass both operands as arguments:


    Complex sum = add (x, y);

Written as an object method, it would take only one argument, which it would add to the current object:


  public Complex add (Complex b) {
    return new Complex (real + b.real, imag + b.imag);
  }

Again, we can refer to the instance variables of the current object implicitly, but to refer to the instance variables of b we have to name b explicitly using dot notation. To invoke this method, you invoke it on one of the operands and pass the other as an argument.


    Complex sum = x.add (y);

From these examples you can see that the current object (this) can take the place of one of the parameters. For this reason, the current object is sometimes called an implicit parameter.



11.6 A modifier

As yet another example, we'll look at conjugate, which is a modifier method that transforms a Complex number into its complex conjugate. The complex conjugate of x + yi is x - yi.

As a class method, this looks like:


  public static void conjugate (Complex c) {
    c.imag = -c.imag;
  }

As an object method, it looks like


  public void conjugate () {
    imag = -imag;
  }

By now you should be getting the sense that converting a method from one kind to another is a mechanical process. With a little practice, you will be able to do it without giving it much thought, which is good because you should not be constrained to writing one kind of method or the other. You should be equally familiar with both so that you can choose whichever one seems most appropriate for the operation you are writing.

For example, I think that add should be written as a class method because it is a symmetric operation of two operands, and it makes sense for both operands to appear as parameters. It just seems odd to invoke the method on one of the operands and pass the other as an argument.

On the other hand, simple operations that apply to a single object can be written most concisely as object methods (even if they take some additional arguments).



11.7 The toString method

There are two object methods that are common to many object types: toString and equals. toString converts the object to some reasonable string representation that can be printed. equals is used to compare objects.

When you print an object using print or println, Java checks to see whether you have provided an object method named toString, and if so it invokes it. If not, it invokes a default version of toString that produces the output described in Section 9.6.

Here is what toString might look like for the Complex class:


  public String toString () {
    return real + " + " + imag + "i";
  }

The return type for toString is String, naturally, and it takes no parameters. You can invoke toString in the usual way:


    String s = x.toString ();

or you can invoke it indirectly through print:


    Complex x = new Complex (1.0, 2.0);
    System.out.println (x);

The output is 1.0 + 2.0i. This version of toString does not look good if the imaginary part is negative. As an exercise, fix it.



11.8 The equals method

When you use the == operator to compare two objects, what you are really asking is, "Are these two things the same object?" That is, do both objects refer to the same location in memory.

For many types, that is not the appropriate definition of equality. For example, two complex numbers are equal if their real parts are equal and their imaginary parts are equal.

When you create a new object type, you can provide your own definition of equality by providing an object method called equals. For the Complex class, this looks like:


  public boolean equals (Complex b) {
    return (real == b.real && imag == b.imag);
  }

By convention, equals is always an object method. The return type has to be boolean.

The documentation of equals in the Object class provides some guidelines you should keep in mind when you make up your own definition of equality:

The equals method implements an equivalence relation:

  • It is reflexive: for any reference value x, x.equals(x) should return true.
  • It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false.
  • For any reference value x, x.equals(null) should return false.

The definition of equals I provided satisfies all these conditions except one. Which one? As an exercise, fix it.



11.9 Invoking one object method from another

As you might expect, it is not only legal, but common to invoke one object method from another. For example, to normalize a complex number, you divide through (both parts) by the absolute value. It may not be obvious why this is useful, but it is.

Let's write the method normalize as an object method, and let's make it a modifier.


  public void normalize () {
    double d = this.abs();
    real = real/d;
    imag = imag/d;
  }

The first line finds the absolute value of the current object by invoking abs on the current object. In this case I named the current object explicitly, but I could have left it out. If you invoke one object method within another, Java assumes that you are invoking it on the current object.

As an exercise, rewrite normalize as a pure function. Then rewrite it as a class method.



11.10 Oddities and errors

If you have both object methods and class methods in the same class definition, it is easy to get confused. A common way to organize a class definition is to put all the constructors at the beginning, followed by all the object methods and all the class methods.

You can have an object method and a class method with the same name, as long as they do not have the same number and types of parameters. As with other kinds of overloading, Java decides which version to invoke by looking at the arguments you provide.

Now that we know what the keyword static means, you have probably figured out that main is a class method, which means that there is no "current object" when it is invoked.

Since there is no current object in a class method, it is an error to use the keyword this. If you try, you might get an error message like: "Undefined variable: this." Also, you cannot refer to the instance variables without using dot notation and providing an object name. If you try you might get "Can't make a static reference to nonstatic variable..." This is not one of the better error messages, since it uses some non-standard language. For example, by "nonstatic variable" it means "instance variable." But once you know what it means, you know what it means.



11.11 Glossary

object method:
A method that is invoked on an object, and that operates on that object, which is referred to by the keyword this in Java or "the current object" in English. Object methods do not have the keyword static.
class method:
A method with the keyword static. Class methods are not invoked on objects and they do not have a current object.
current object:
The object on which an object method is invoked. Inside the method, the current object is referred to by this.
this:
The keyword that refers to the current object.
implicit:
Anything that is left unsaid or implied. Within an object method, you can refer to the instance variables implicitly (without naming the object).
explicit:
Anything that is spelled out completely. Within a class method, all references to the instance variables have to be explicit.