使用反射来验证特征的所有实例都具有唯一字段
Using reflection to verify all instances of a trait have a unique field
考虑以下代码。动物应该有一个唯一的 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 获取 classes。
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))
我开始怀疑这可能是不可能的,但很想是错的!我有 classes 但无法实例化它们的实例,因为所有构造函数都不同,我不确定是否可以仅从 class.[=15 访问 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 持有实例并且该实例将具有 uniqueId
字段和 getter 方法。
未经测试,像这样的东西应该可以工作:
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
在不改变结构的情况下,我会考虑一种不同的方法:自己提供实例,只需验证它们包含所有 类.
val instances = Set(Dog(0, ""), Cat(""), Salmon(true))
// allSubtypes defined in the question
assert(allSubtypes.toSet == instances.map(_.getClass))
// same logic as above but we already have a Set
assert(instances.map(_.uniqueId).size == instances.size)
考虑以下代码。动物应该有一个唯一的 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 获取 classes。
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))
我开始怀疑这可能是不可能的,但很想是错的!我有 classes 但无法实例化它们的实例,因为所有构造函数都不同,我不确定是否可以仅从 class.[=15 访问 uniqueId
=]
正如 Tomer Shetah 的评论所说,这似乎不可能如前所述。但如果您可以拆分 Animal
和 AnimalCompanion
,并使每个 AnimalCompanion
:
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 持有实例并且该实例将具有 uniqueId
字段和 getter 方法。
未经测试,像这样的东西应该可以工作:
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
在不改变结构的情况下,我会考虑一种不同的方法:自己提供实例,只需验证它们包含所有 类.
val instances = Set(Dog(0, ""), Cat(""), Salmon(true))
// allSubtypes defined in the question
assert(allSubtypes.toSet == instances.map(_.getClass))
// same logic as above but we already have a Set
assert(instances.map(_.uniqueId).size == instances.size)