Scala 2.11最小改动实现Landscape复用Compass的方案
问题背景
使用Scala 2.11.12版本。
代码库中原有定义如下Landscape样例类:
case class Landscape( north: Sight, east: Sight, south: Sight, west: Sight ) { def toList: List[Sight] = List(north, east, south, west) def isIdyllic: Boolean = north.isPastoral && east.isPastoral && south.isPastoral && west.isPastoral }
其中Sight为自定义样例类,对应伴生对象定义如下:
object Landscape { def fromSeq(s: Seq[Sight]): Landscape = { require(s.length == 4) Landscape( north = s(0), east = s(1), south = s(2), west = s(3) ) } def pickByBeautifulSouth(scape1: Landscape, scape2: Landscape): Landscape = { if (scape1.south.beauty > scape2.south.beauty) scape1 else scape2 } }
后续因需要使用多个结构相似的类型,抽取了泛型样例类Compass[A]:
case class Compass[A]( north: A, east: A, south: A, west: A ) { def toList: List[A] = List(north, east, south, west) }
对应泛型伴生对象:
object Compass { def fromSeq[A](s: Seq[A]): Compass[A] = { require(s.length == 4) Compass[A]( north = s(0), east = s(1), south = s(2), west = s(3) ) } }
isIdyllic和pickByBeautifulSouth是仅针对Sight类型的专属方法,对任意泛型A无意义。目标是让Landscape复用Compass的toList、fromSeq能力,同时完全兼容原有调用方式:支持Landscape(sight1, sight2, sight3, sight4)构造、Landscape.copy(west=someSight)拷贝、Landscape.pickByBeautifulSouth(scape1, scape2)伴生方法调用,改动量最小。
实现方案
不需要修改原有Compass样例类及伴生对象的任何代码,仅需通过类型别名+手动定义伴生对象+值类隐式扩展的组合实现,完全满足所有兼容要求:
// 定义类型别名,将Landscape等价为Compass[Sight] type Landscape = Compass[Sight] // 手动定义Landscape伴生对象,对齐原有调用逻辑 object Landscape { // 转发构造方法,兼容原有Landscape(...)入参调用 def apply(north: Sight, east: Sight, south: Sight, west: Sight): Landscape = Compass(north, east, south, west) // 直接复用Compass的fromSeq实现,无需重复编写逻辑 def fromSeq(s: Seq[Sight]): Landscape = Compass.fromSeq(s) // 保留原有专属伴生方法 def pickByBeautifulSouth(scape1: Landscape, scape2: Landscape): Landscape = { if (scape1.south.beauty > scape2.south.beauty) scape1 else scape2 } // 值类扩展实例专属方法,无额外对象包装开销 implicit class LandscapeOps(private val ls: Landscape) extends AnyVal { def isIdyllic: Boolean = ls.north.isPastoral && ls.east.isPastoral && ls.south.isPastoral && ls.west.isPastoral } }
兼容性验证
- 构造调用:
Landscape(s1, s2, s3, s4)可直接使用,伴生对象中定义的apply方法和原样例类生成的apply签名完全一致。 - copy方法:
Landscape本质是Compass[Sight]样例类,自动自带样例类的copy实现,scape.copy(west=someSight)调用完全不受影响,返回值类型自动匹配Landscape。 - 通用方法复用:
toList直接继承自Compass实例方法,fromSeq直接转发Compass伴生方法,无重复代码。 - 专属方法可用:实例调用
.isIdyllic时编译器会自动通过隐式值类识别,伴生对象的pickByBeautifulSouth调用方式和原有逻辑完全一致。 - 性能无损耗:隐式类继承
AnyVal实现值类优化,Scala 2.11原生支持,不会产生额外的对象包装开销。
之前尝试类型别名方案时出现apply方法丢失的问题,核心原因是Scala的类型别名不会自动关联对应类型的伴生对象,只要手动定义同名伴生对象并转发必要的构造方法,就可以完全对齐原有调用逻辑,上层业务代码不需要做任何修改。
内容的提问来源于stack exchange,提问作者TheGreenHeptagon
相关产品推荐
相关产品推荐

