【发布时间】:2015-04-13 02:59:29
【问题描述】:
我使用的是 Scala 2.10.4。
请直接打个比方——实际的代码深深嵌入在一个复杂的程序中,所以我不会解释这个问题,而是以一种由来已久的方式来抽象这个问题来谈论动物 ;-)
在 scala 中,我有 2 个特征 - 例如:
动物和HouseBase。
我无法更改 Animal,但我使用 Dog、Rabbit、Fish 等类从它继承。恼人的是,我无法更改每个子类,因为我不拥有我使用的所有子类。
我的动物都住在某个地方——它们的家必须继承自 HouseBase。我可以更改 HouseBase 及其子类(如果必须的话,可以通过另一层抽象)。
所以 Dog 是 Animal 的子类,并且会生活在作为 HouseBase 子类的 Kennel 中。
兔子会住在笼子里,鱼会住在缸里。
请注意,这里没有强制执行 1:1 关系 - 鱼也可以生活在池塘中,我们也必须能够处理。
我希望的是——给定一个具体的动物(例如 Fish),通过抽象类型 Animal 引用,并给定一个具体的返回类型(例如 Tank),Scala 将能够自动选择正确的我在下面的设计中的隐式参数。
object AnimalSelectionProblem extends App {
def abstractFish : Animal = new Fish(true, 20.0)
def concreteFish : Fish = new Fish(false, 30.0)
def abstractDog : Animal = new Dog("tasty bone")
def concreteDog : Dog = new Dog("yummy bone")
def abstractRabbit : Animal = new Rabbit(5)
def concreteRabbit : Rabbit = new Rabbit(10)
import HouseImplicits._
val myTank1: Tank = HouseImplicits.create(abstractFish)
val myTank2: Tank = HouseImplicits.create(concreteFish)
val myKennel1: Kennel = HouseImplicits.create(abstractDog)
val myKennel2: Kennel = HouseImplicits.create(concreteDog) // This works
val myhutch1: Hutch = HouseImplicits.create(abstractRabbit)
val myhutch2: Hutch = HouseImplicits.create(concreteRabbit) // This works
}
但是有两个相关的问题。
问题 1 - 如果作为抽象引用动物,则隐式参数将仅查找采用抽象类型(动物)而不是底层具体类型的函数。我怀疑解决方案可能是使用 ClassTags,因为 Scala 似乎不使用运行时信息?我尝试实现了这一点,但最终迷失了方向(我对 Scala 还很陌生!)。
问题 2 - 如果我的动物可以住在不止一种类型的 House 中,则会出现类似的问题,即使指定了具体的返回类型,编译器也会发现 Fish 的 2 个隐含对象不明确。我有点困惑在这里做什么!
我可以使用手动样板来设计解决方案以在运行时匹配类型,但这不是很可扩展。
感谢您的任何想法!其余代码如下。
编辑 - 这些链接似乎证实了我的怀疑。使用了编译时多态性,因此无法知道运行时类型:
http://like-a-boss.net/2013/03/29/polymorphism-and-typeclasses-in-scala.html
所以,我想我现在的问题是,鉴于此,有没有办法修改我的示例以使用运行时调度?
动物:
trait Animal {
}
class Dog(val boneName: String) extends Animal
class Rabbit(val length: Int) extends Animal
class Fish(val likesFrogs: Boolean, val optimumTemp: Double) extends Animal
Houses and Implicits:
sealed trait HouseBase
// Made up some arbitrary member variables
case class Kennel(posessions: Seq[String]) extends HouseBase
case class Hutch(length: Int) extends HouseBase
case class Tank(waterTemp: Double) extends HouseBase
case class Pond(containsFrog: Boolean) extends HouseBase
sealed trait HouseCreator[A <: Animal, HB <: HouseBase] {
def create(animal: A): HB
}
object HouseImplicits {
implicit object BuildKennelForDog extends HouseCreator[Dog, Kennel] {
override def create(dog: Dog): Kennel = {
new Kennel(Seq(dog.boneName))
}
}
implicit object BuildTankForFish extends HouseCreator[Fish, Tank] {
override def create(fish: Fish): Tank = {
new Tank(fish.optimumTemp)
}
}
implicit object BuildPondForFish extends HouseCreator[Fish, Pond] {
override def create(fish: Fish): Pond = {
new Pond(fish.likesFrogs)
}
}
implicit object BuildHutchForRabbit extends HouseCreator[Rabbit, Hutch] {
override def create(rabbit: Rabbit): Hutch = {
new Hutch(rabbit.length*5)
}
}
def create[A <: Animal, H <: HouseBase](animal: A)(implicit house: HouseCreator[A,H]) : H = {
val newHouse = house.create(animal)
newHouse
}
}
【问题讨论】:
标签: scala reflection polymorphism erasure implicit-parameters