问题描述
考虑以下代码。动物应该有一个唯一的ID。我想动态验证(在测试中)Animal 的所有具体子类型都具有唯一 ID。我希望我的测试失败,例如,如果 Cat 和 Fish 都试图通过 uniqueId
= 2。
sealed trait Animal {
val uniqueId: Int
}
abstract class Mammal(val uniqueId: Int) extends Animal
abstract class Fish(val uniqueId: Int) extends Animal
case class Dog(age: Int,name: String) extends Mammal(1)
case class Cat(favouriteFood: String) extends Mammal(2)
case class Salmon(isCute: Boolean) extends Fish(3)
我正在使用 reflections 获取课程。
import org.reflections.Reflections
val reflections = new Reflections("package.blah")
val allSubtypes: Seq[Class[_ <: Animal]] = reflections.getSubTypesOf(classOf[Animal]).asScala.toList
val concreteSubtypes: Seq[Class[_ <: Animal]] = allSubtypes.filter(c => !Modifier.isAbstract(c.getModifiers))
我开始怀疑这可能是不可能的,但我很想错!我有这些类,但无法实例化它们的实例,因为所有构造函数都不同,而且我不确定是否可以仅从类访问 uniqueId
。
解决方法
正如 Tomer Shetah 的评论所说,这似乎不可能如所述。但是,如果您可以拆分 Animal
和 AnimalCompanion
,并使每个 AnimalCompanion
的 id 实际上唯一,它可能会起作用:
abstract class AnimalCompanion(val uniqueId: Int)
sealed trait Animal {
def companion: AnimalCompanion
def uniqueId = companion.uniqueId
}
abstract class Mammal(val companion: AnimalCompanion) extends Animal
abstract class Fish(val companion: AnimalCompanion) extends Animal
case class Dog(age: Int,name: String) extends Mammal(Dog)
object Dog extends AnimalCompanion(1)
case class Cat(favouriteFood: String) extends Mammal(Cat)
object Cat extends AnimalCompanion(2)
case class Salmon(isCute: Boolean) extends Fish(Salmon)
object Salmon extends AnimalCompanion(3)
然后您以相同的方式找到 AnimalCompanion
的所有子类型,并且因为它们都是 object
,所以它们将 have the MODULE$
field 持有实例,并且该实例将具有 {{1 }} 字段和 getter 方法。
没有测试,这样的事情应该可以工作:
uniqueId
然后仍然要检查它们实际上是唯一的,例如通过
val allCompanionClasses = reflections.getSubTypesOf(classOf[AnimalCompanion]).asScala
val allCompanionInstances = allCompanionClasses.map(_.getField("MODULE$").get(null))
val allUniqueIds = allCompanionInstances.map(x => x.getClass().getMethod("uniqueId").invoke(x))
在不改变结构的情况下,我会考虑另一种方法:自己提供实例,只需验证它们包含所有类。
allUniqueIds.toSet.size == allUniqueIds.size