令我惊讶的是,如果不使用第三方库,Scala就无法轻松解决此类问题。请注意,链接的重复问题无法满足以下要求。
我是一个团队的成员,该团队最近继承了Scala项目,并希望使代码更加干燥。我有2个相同的函数,但是采用并返回不同的case类。 一个函数是否可以使用任一案例类?
我正在寻找使用标准Scala的解决方案,而无需安装第三方库。
我已经尝试了很多方法来抽象该函数,但都没有碰到运气(即使用泛型类型,使用Either
的{{1}}类型接受两个案例类)。
这是一个虚拟的例子来说明我的问题:
asInstanceOf
根据以下Mario的答案进行详细说明。如何消除重复条件逻辑的需要?:
trait Bird {
val avgHeight: Int
}
case class Pigeon(avgHeight: Int) extends Bird
case class Ostrich(avgHeight: Int) extends Bird
def updateHeight(bird: ?): ? = {
bird.copy(avgHeight = 2)
}
/*
def updateHeight[T <: Bird](bird: T): T = {
val chosenBird = bird match {
case _: Pigeon => bird.asInstanceOf[Pigeon]
case _: Ostrich => bird.asInstanceOf[Ostrich]
}
chosenBird.copy(avgHeight = 2)
}
*/
println(updateHeight(Pigeon(1)))
println(updateHeight(Ostrich(1)))
我正在寻找一个真正的DRY示例,其中的主代码不需要重复:这是打字稿中的一个示例:https://repl.it/repls/PlayfulOverdueQuark
sealed trait Bird {
val avgHeight: Int
val avgWidth: Int
val wingSpan: Int
}
case class Pigeon(avgHeight: Int, avgWidth: Int, wingSpan: Int) extends Bird
case class Ostrich(avgHeight: Int, avgWidth: Int, wingSpan: Int) extends Bird
def updateBird(bird: Bird, height: Int, span: Int): Bird = {
bird match {
case p: Pigeon =>
var newPigeon = p.copy(avgHeight = height)
if (p.avgWidth.equals(0)) {
newPigeon = newPigeon.copy(avgWidth = 100)
}
if (span > 0) {
newPigeon = newPigeon.copy(wingSpan = span * 2)
}
newPigeon
case o: Ostrich =>
var newOstrich = o.copy(avgHeight = height)
if (o.avgWidth.equals(0)) {
newOstrich = newOstrich.copy(avgWidth = 100)
}
if (span > 0) {
newOstrich = newOstrich.copy(wingSpan = span * 2)
}
newOstrich
}
}
updateBird(Pigeon(1, 2, 3), 2, 0) // Pigeon(2,2,3)
updateBird(Ostrich(1, 0, 3), 2, 4) // Ostrich(2,100,8)
答案 0 :(得分:2)
以下是根据Harald Gliebe和Thilo的建议使用shapeless lenses的示例:
import shapeless._
object Hello extends App {
sealed trait Bird {
val avgHeight: Int
}
case class Pigeon(avgHeight: Int) extends Bird
case class Ostrich(avgHeight: Int) extends Bird
implicit val pigeonLens = lens[Pigeon].avgHeight
implicit val ostrichLens = lens[Ostrich].avgHeight
def updateHeight[T <: Bird](bird: T, height: Int)(implicit birdLense: Lens[T, Int]): T =
birdLense.set(bird)(height)
println(updateHeight(Pigeon(1), 2))
println(updateHeight(Ostrich(1), 2))
}
输出
Pigeon(2)
Ostrich(2)
链接的打字稿示例使用可变状态来实现updateHeight
,但是case类是不可变的结构。我们可以达到类似的目的
sealed trait Bird {
val avgHeight: Int
}
case class Pigeon(avgHeight: Int) extends Bird
case class Ostrich(avgHeight: Int) extends Bird
def updateHeight(bird: Bird, height: Int): Bird =
bird match {
case _: Pigeon => Pigeon(height)
case _: Ostrich => Ostrich(height)
}
updateHeight(Pigeon(1), 2)
updateHeight(Ostrich(1), 2)
输出
res0: Bird = Pigeon(2)
res1: Bird = Ostrich(2)
请注意编译时间类型是Bird
,而运行时类型是特殊的Pigeon
或Ostrich
。
如果问题实际上是关于如何对不可变的案例类进行突变,那么我们可以简单地使用copy
来创建一个高度变化的新实例,
Pigeon(1).copy(avgHeight = 2)
Ostrich(1).copy(avgHeight = 2)
输出
res2: Pigeon = Pigeon(2)
res3: Ostrich = Ostrich(2)
但是,如果您想像打字稿示例中那样使用不可变状态,请尝试
class Bird(var avgHeight: Int)
class Pigeon(avgHeight: Int) extends Bird(avgHeight)
class Ostrich(avgHeight: Int) extends Bird(avgHeight)
def updateHeight(bird: Bird, height: Int): Bird = {
bird.avgHeight = height
bird
}