说我有一份动物清单,我想把它们分开:
BasicAnimal = {Cat, Dog}
Carnivore = {Cat, Dog, Dragon}
Herbivore = {Cat, Dog, Horse}
现在,这些动物也必须住在某个地方。所以有一个
BasicShelter with a method shelter(animal: BasicAnimal)
Den with a method shelter(animal: Carnivore)
Shed with a method shelter(animal: Herbivore)
在Scala中实现此功能的最佳方法是什么?一次尝试是:
class BasicAnimal extends Enumeration{
val Cat, Dog = Value
}
class Carnivore extends BasicAnimal{
val Dragon = Value
}
class Herbivore extends BasicAnimal{
val Horse = Value
}
然后
class BasicHouse{
def shelter(animal: BasicAnimal) = {//lots of code}
}
class Den{
def shelter(animal: Carnivore) = {
//again lots of code, but the cases dealing with Cat and Dog can be relegated to super.shelter
}
}
class Shed{
//the same
}
可悲的是,这不起作用。来自食肉动物的狗与BasicAnimal中的狗不同。那就是Carnivore.Dog == BasicAnimal.Dog返回false,因此,在Den中重用BasicHouse代码的唯一方法是使用一个相当hacky的平等方法来比较枚举的字符串(或类似的东西)。它有效,但它非常不洁净。你能看到其他任何可能吗?
答案 0 :(得分:9)
根据@ paradigmatic的回答,但有一些改进:
sealed abstract trait BasicAnimal
sealed abstract trait Carnivore extends BasicAnimal
sealed abstract trait Herbivore extends BasicAnimal
sealed abstract trait Omnivore extends Carnivore with Herbivore
case object Dog extends Omnivore
case object Cat extends Omnivore
case object Dragon extends Carnivore
case object Horse extends Herbivore
使特征抽象允许您利用模式匹配中的完整性检查(否则会警告未尝试匹配特征本身)
Omnivore
特征会删除一些重复,也有助于模式匹配
使用extends
代替自我输入只是更简洁,更直观
case object
代替object
主要是为了更好地记录代码的意图,并提供toString
答案 1 :(得分:5)
枚举的另一种解决方案是使用sealed trait
来定义枚举。
用你的例子:
sealed trait BasicAnimal
sealed trait Carnivore { self: BasicAnimal => }
sealed trait Herbivore { self: BasicAnimal => }
object Dog extends BasicAnimal with Carnivore with Herbivore
object Cat extends BasicAnimal with Carnivore with Herbivore
object Dragon extends BasicAnimal with Carnivore
object Horse extends BasicAnimal with Herbivore
这比Enumeration更灵活,但你失去了轻易枚举所有值的可能性。