自定義字串插值器

除內建字串插補器外,還可以定義自定義字串插值器。

my"foo${bar}baz"

由編譯器擴充套件為:

new scala.StringContext("foo", "baz").my(bar)

scala.StringContext 沒有 my 方法,因此它可以通過隱式轉換提供。然後將實現與內建 s 插值器具有相同行為的自定義插值器,如下所示:

implicit class MyInterpolator(sc: StringContext) {
  def my(subs: Any*): String = {
    val pit = sc.parts.iterator
    val sit = subs.iterator
    // Note parts.length == subs.length + 1
    val sb = new java.lang.StringBuilder(pit.next())
    while(sit.hasNext) {
      sb.append(sit.next().toString)
      sb.append(pit.next())          
    }
    sb.toString
  }
}

插值 my"foo${bar}baz" 將 desugar:

new MyInterpolation(new StringContext("foo", "baz")).my(bar)

請注意,插值函式的引數或返回型別沒有限制。這導致我們走下了一條黑暗的道路,其中可以創造性地使用插值語法來構造任意物件,如以下示例所示:

case class Let(name: Char, value: Int)

implicit class LetInterpolator(sc: StringContext) {
  def let(value: Int): Let = Let(sc.parts(0).charAt(0), value)
}

let"a=${4}" // Let(a, 4)
let"b=${"foo"}" // error: type mismatch
let"c=" // error: not enough arguments for method let: (value: Int)Let