使用反射来验证特征的所有实例都有一个唯一的字段

问题描述

考虑以下代码。动物应该有一个唯一的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 的评论所说,这似乎不可能如所述。但是,如果您可以拆分 AnimalAnimalCompanion,并使每个 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