磁铁模式(Magnet Pattern)是Scala中独有的实现函数重载的一种模式。它是由spray框架的设计者提出的,在spray框架中大量使用。
磁铁模式有什么用?
我们设计API时,需要尽量简洁。有时表达的语义是一样的,但是参数的类型不同,怎么办?一般的做法就是利用函数重载。但Java和Scala中的函数重载存在一些问题:
- JVM的类型擦除机制会导致认不出高阶参数的重载。比如:
// 类型擦除导致函数重载冲突,编译报错 def notWork(ls: List[Int]): List[Int] = ls.map(_ * 2) def notWork(ls: List[String]): List[String] = ls ++ ls // ERROR: double definition
- 不方便扩展。增加对新的参数类型时,必须在API中定义。
磁铁模式可以很优雅的解决这些问题——把“重载”的函数,像磁铁一样,一块一块贴上去。
磁铁模式怎么做?
磁铁模式利用了Scala语言的隐式转换的特性。模式的结构如下:

- Client中定义一个API,型如:
def operation(magnet: Magnet): magnet.Result
- Magnet是个接口(特质),其中需要定义一个抽象类型、一个抽象方法。
traitMagnet { type Result def apply(): Result }
- 剩下的,就是定义一系列不同的隐式转换类,把不同的类型都隐式转换成Magnet类型,进而实现了函数重载的效果。例如:
implicit class fromInt(x: Int) extends Magnet { override type Result = Int override def apply(): Result = x * 2 }
一个完整的例子
假设,现在我们要设计一个接口叫做Doubling,它其中的double方法可以把传入的参数“翻倍”。具体怎么翻倍,不同类型有不同的翻法。比如Int型就是乘以2、String型就是变成两个相同的字符串连在一起、List[Int]就是把列表中的每个元素都乘以2……
我们可以这样实现(完整的代码可以看这里)
class Doubling {
def double(magnet: DoubleMagnet): magnet.Result = magnet()
}
// Magnet Interface
trait DoubleMagnet {
type Result
def apply(): Result
}
// Implicit Conversions
object DoubleMagnet {
implicit class fromInt(x: Int) extends DoubleMagnet {
override type Result = Int
override def apply(): Result = x * 2
}
implicit class fromListInt(ls: List[Int]) extends DoubleMagnet {
override type Result = List[Int]
override def apply(): Result = ls.map(_ * 2)
}
implicit class fromListString(ls: List[String]) extends DoubleMagnet {
override type Result = List[String]
override def apply(): Result = ls ++ ls
}
// overloading with different number of parameters
implicit class fromStringIntTuple(para: Tuple2[String, Int]) extends DoubleMagnet {
override type Result = String
override def apply(): String = para._1 * para._2
}
}
测试一下效果吧:
object App extends App {
val doubling = new Doubling()
println(doubling.double(2))
println(doubling.double(List(1, 2, 3)))
println(doubling.double(List("a", "b", "c")))
println(doubling.double("a", 5))
}
运行结果如下:
4
List(2, 4, 6)
List(a, b, c, a, b, c)
aaaaa
网友评论