Callback method

Callback method #

When a method takes another method as argument, the latter is a callback method.

Anonymous methods are often used as callback methods.

in Java #

Running example #

Example. The two following Java methods have the same structure: both modify each unit in the input list.

void boostUnits(List<Unit> units){
  for(Unit unit: units){
      unit.health++;
  }
}

void penalizeGreenUnits(List<Unit> units){
    for(Unit unit: units){
        if(unit.color.equals("green")){
            unit.health--;
        }
    }
}

In each case, let us isolate the modification that is applied to a single unit, with a dedicated method (called boost and penalizeGreen respectively).

void boost(Unit unit){
    unit.health++;
}

void penalizeGreen(Unit unit){
    if(unit.color.equals("green")){
        unit.health--;
    }
}

Both methods have type Unit $\to$ void, so they are instances of the functional interface Consumer<Unit>.

This allows us to factorize our code with a method transform that takes as input:

  • an array of units,
  • a callback method with type Consumer<Unit>,

and applies the callback method to each unit in the input array.

void transform(List<Unit> units, Consumer<Unit> method){
    for (Unit unit: units){
        method.accept(unit);
    }
}

Passing a callback method #

A callback method in Java can be passed as:

Method reference as a callback method #

Syntax. A Java method reference is written:

  • ClassName::methodName for a static method,
  • objectVariable::methodName for an instance method,
  • ClassName::new for a constructor.

Example (continued).

Let us assume that our methods boost and penalizeGreen above are static methods of some class called UnitTransformer:

public class UnitTransformer{

    static void boost(Unit unit){
        unit.health++;
    }

    static void penalizeGreen(Unit unit){
        if(unit.color.equals("green")){
            unit.health--;
        }
    }
}

We can call our method transform with boost as callback method:

List<Unit> units = getUnits();

transform(
    units,
    UnitTransformer::boost
);

And similarly with penalizeGreen:

List<Unit> units = getUnits();

transform(
    units,
    UnitTransformer::penalizeGreen
);

Lambda expression as a callback method #

Alternatively, instead of declaring a method boost, we can directly use a lambda expression:

Example (continued).

List<Unit> units = getUnits();

transform(
    units,
    unit -> unit.health++
);

And similarly, instead of declaring a method penalizeGreen, we can use the following lambda expression:

List<Unit> units = getUnits();

transform(
    units,
    unit -> {
        if(unit.color.equals("green")){
          unit.health-- ;
        }
      }
);

Native support #

Programming languages that support anonymous methods (like Java, Python, C#, C++, JavaScript, etc.) often provide concise syntaxes to use them as callback methods.

Example(continued).

In Java, the above method transform is not necessary. Instead, we can use the native method forEach, available (among others) for any Java Collection. This method takes a Consumer method as input, and applies this method to each element of the collection.

So the following program:

getUnits().forEach(unit -> unit.health++);

is equivalent to:

transform(
    getUnits(),
    unit -> unit.health++
);

void transform(List<Unit> units, Consumer<Unit> method){
    for (Unit unit: units){
        method.accept(unit);
    }
}