Instance method

Instance method #

In many object-oriented languages (like Java), methods are implemented within classes.

An instance method is called via an instance of the class where the method is declared.

In Java #

Example. Consider an instance method myMethod() declared in a class MyClass:

public class MyClass {
    ...
    public MyClass(){
        ...
    }

    public void myMethod(){
        ...
    }
}

This method can be called by appending . to a variable myVar of type MyClass, as follows:

MyClass myVar = new MyClass();
myVar.myMethod();

The object used to call an instance method can be accessed within this method.

Example (continued). The object referenced by the variable myVar is accessible within myMethod (even though it is not passed as argument).

Benefit. This intuitively lets us write a method with one less argument.

Example. Recall the imaginary game introduced earlier. The method meets that we implemented is not and instance method.

Instead, we can implement it as instance method meetsWith, as follows:

public abstract class NPC {
    int xp;
    ...

    public void meetsWith(NPC contact){
      // same implementation as for `meet`
      int tmp = xp;
      xp += contact.xp;
      contact.xp += tmp;
    }
}

This method may be called as follows:

Unicorn u = new Unicorn(3);
Butterfly b = new Butterfly(5);

u.meetsWith(b);

Overriding #

Terminology. A same instance method $m$ can be declared in a class $C$ and a subclass $S$ or $C$. In this case, we say that $S$ overrides $m$.

When such a method is called, the most specific applicable version is executed.

Example (continued). Let us extend our example with an instance method learn, declared in both NPC and MobileNPC, as follows:

public abstract class NPC {
  int xp;
  ...

  public void learn(){
    if(xp < 10){
      xp += 2;
    }
  }
}

$\qquad$

public abstract class MobileNPC extends NPC {
  ...

  public void learn (){
    if(xp < 10){
      xp += 2;
    }
    xp += 1;
  }
}

The following increases the XP of (the object referenced by) u by 3, because Unicorn is a subclass of MobileNPC.

Unicorn u = new Unicorn(3);
u.learn();      // the implementation of `MobileNPC` is executed

In contrast, the following increases the XP of (the object referenced by) f by 2, because Flower is a subclass of NPC, but not a subclass of MobileNPC.

Flower f = new Flower();
f.learn();      // the implementation of `NPC` is executed

Syntax. We can use the annotation @Override to indicate an overriding method, as follows:


public abstract class MobileNPC extends NPC {
    ...

    @Override
    public void learn(){
        if(xp < 10){
            xp += 2;
        }
        xp += 1;
    }
}

This is not necessary, but considered good practice.

A benefit is that the program will not compile if the overridden and overriding methods have different signatures.

Dynamic dispatch (a.k.a. runtime polymorphism) #

Dynamic dispatch determines at run time which version of a method must be executed, if this cannot be determined by the compiler. This is a feature of most (class-based) object-oriented languages.

Example (continued). Assume a method generateRandomNPCs that generates a random array of NPCs (butterflies, unicorns or flowers).

Then call the method learn for each NPC in such an array:

NPC[] ramdomNPCs = generateRandomNPCs();
for (NPC c: randomNPCs){
    c.learn();
}

The most specific applicable version of the method learn will be executed for each NPC, based on its type, even though this type cannot be determined at compile time.

For instance, if the array contains an instance of Unicorn, then the method MobileNPC.learn() will be executed for it (rather than the method NPC.learn()).

Code factorization #

An overriding method often extends the functionality of the overridden one. This is a possible source of duplicate code.

Example (continued). Both implementations of learn() contain:

if(xp < 10){
    xp += 2;
}

A common way to factorize this consists in calling the overridden method inside the overriding one.

Syntax. The keyword super (seen earlier for constructors) lets us to distinguish an overridden method from an overriding one (since they have the same name).

Example (continued). We can avoid duplicate code in the overriding method, as follows:

public abstract class MobileNPC extends NPC {
    ...

    @Override
    public void learn() {
        super.learn();
        xp += 1;
    }
}

In this example, what would be the effect of replacing super.learn() with learn()?

Extend our model with a method that lets an NPC spy on an NPC, such that:

  • the spying NPC gains the XP of the one being spied on,
  • a flower cannot be spied on, and
  • when spying, a butterfly also gets to learn (by executing the method learn()).