在 Scala 中实现引用
C++ 里引用是一个非常有用的工具。虽然 Scala 没有提供引用作为原生语法,但是我们能够轻松的使用不到 100 行代码来模拟出 C++ 的引用:
trait Ref[+A] {
def get: A
override def toString: String = s"Ref($get)"
trait MutableRef[A] {
def get: A
def set(value: A): Unit
def update(value: A): Unit = set(value)
override def toString: String = s"MutableRef($get)"
import scala.language.experimental.macros
import scala.language.implicitConversions
import scala.reflect.macros.blackbox
implicit def &[A](value: => A): Ref[A] = new Ref[A] {
override def get: A = value
implicit def &&[A](value: => A): MutableRef[A] = macro refMacro[A]
implicit def *[A](ref: Ref[A]): A = ref.get
implicit def *[A](ref: MutableRef[A]): A = ref.get
def refMacro[A: c.WeakTypeTag](c: blackbox.Context)(value: c.Tree): c.Tree = {
import c.universe._
val tpe = weakTypeOf[A]
q"""
new MutableRef[$tpe] {
def get: $tpe = $value
def set(value: $tpe): Unit = $value = value
implicit def mutableRef2ref[A](mutableRef: MutableRef[A]): Ref[A] = &(mutableRef.get)
然后我们可以做几个实验:
//immutable reference
var a: Int = 10
val aRef: Ref[Int] = a
assert(aRef.get == 10)
a = 100
assert(aRef.get == 100)
//mutable reference
var b: Int = 10
val bRef: MutableRef[Int] = b
assert(bRef.get == 10)
b = 100
assert(bRef.get == 100)
bRef.set(50)
assert(b == 50)
//swap
def swap[A](a1: MutableRef[A], a2: MutableRef[A]): Unit = {
val n = a1.get
a1.set(a2.get)
a2.set(n)
var a1 = 10