This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 5.9, “How to support a fluent style of programming in Scala.”
Problem
You want to create a Scala API so developers can write code in a fluent programming style, also known as method chaining.
Solution
A fluent style of programming lets users of your API write code by chaining method calls together, as in this example:
person.setFirstName("Leonard") .setLastName("Nimoy") .setAge(82) .setCity("Los Angeles") .setState("California")
To support this style of programming:
- If your class can be extended, specify
this.type
as the return type of fluent style methods. - If you’re sure that your class won’t be extended, you can optionally return
this
from your fluent style methods.
Returning this.type
The following code demonstrates how to specify this.type
as the return type of the set*
methods:
class Person { protected var fname = "" protected var lname = "" def setFirstName(firstName: String): this.type = { fname = firstName this } def setLastName(lastName: String): this.type = { lname = lastName this } } class Employee extends Person { protected var role = "" def setRole(role: String): this.type = { this.role = role this } override def toString = { "%s, %s, %s".format(fname, lname, role) } }
The following test object demonstrates how these methods can be chained together:
object Main extends App { val employee = new Employee // use the fluent methods employee.setFirstName("Al") .setLastName("Alexander") .setRole("Developer") println(employee) }
Discussion
If you’re sure your class won’t be extended, specifying this.type
as the return type of your set*
methods isn’t necessary; you can just return the this
reference at the end of each fluent style method. This is shown in the addTopping
, setCrustSize
, and setCrustType
methods of the following Pizza
class, which is declared to be final
:
final class Pizza { import scala.collection.mutable.ArrayBuffer private val toppings = ArrayBuffer[String]() private var crustSize = 0 private var crustType = "" def addTopping(topping: String) = { toppings += topping this } def setCrustSize(crustSize: Int) = { this.crustSize = crustSize this } def setCrustType(crustType: String) = { this.crustType = crustType this } def print() { println(s"crust size: $crustSize") println(s"crust type: $crustType") println(s"toppings: $toppings") } }
The use of this class is demonstrated with the following driver program:
object FluentPizzaTest extends App { val p = new Pizza p.setCrustSize(14) .setCrustType("thin") .addTopping("cheese") .addTopping("green olives") .print() }
That code results in the following output:
crust size: 14 crust type: thin toppings: ArrayBuffer(cheese, green olives)
Returning this
in your methods works fine if you’re sure your class won’t be extended, but if your class can be extended—as in the first example where the Employee
class extended the Person
class—explicitly setting this.type
as the return type of your set*
methods ensures that the fluent style will continue to work in your subclasses. In this example, this makes sure that methods like setFirstName
on an Employee
object return an Employee
reference and not a Person
reference.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
See Also
- Definition of a fluent interface
- Method chaining
- Martin Fowler’s discussion of a fluent interface