确定子类中的非空附加字段

Determine non-empty additional fields in a subclass

假设我有一个看起来像这样的特征

trait MyTrait {
  val x: Option[String] = None
  val y: Option[String] = None
}

Post 定义特征我将这个特征扩展到 class MyClass 看起来像这样

case class MyClass(
  override val x: Option[String] = None, 
  override val y: Option[String] = None, 
  z: Option[String] = None
) extends MyTrait

现在我需要找出除 MyTrait 扩展的属性之外的任何其他 属性 是否不是 None。从某种意义上说,如果我需要编写一个名为 getClassInfo 的方法,其中 returns true/false 基于案例 class 中存在的值。在这种情况下,如果 z 是非可选的,它应该 return 为真。我的 getClassInfo 是这样的

def getClassInfo(myClass: MyClass): Boolean = {
  myClass
    .productIterator
    .filterNot(x => x.isInstanceOf[MyTrait])
    .exists(_.isInstanceOf[Some[_]])
}

在这种情况下,理想情况下,这应该过滤掉不属于 Mytrait 和 return 我 z 的所有字段。 我尝试使用方差,但似乎 isInstanceOf 不采用相同的

filterNot(x => x.isInstanceOf[+MyTrait])

然而这是不可能的

val a = getClassInfo(MyClass()) //Needs to return false
val b = getClassInfo(MyClass(Some("a"), Some("B"), Some("c"))) //returns true
val c = getClassInfo(MyClass(z = Some("z"))) //needs to return true
val d = getClassInfo(MyClass(x = Some("x"), y = Some("y"))) // needs to return false

简单的答案是声明一个给出你想要的结果的抽象方法并在子类中覆盖它:

trait MyTrait {
  def x: Option[String]
  def y: Option[String]
  def anyNonEmpty: Boolean = false
}

case class MyClass(x: Option[String] = None, y: Option[String] = None, z: Option[String] = None) extends MyTrait {
  override def anyNonEmpty = z.nonEmpty
}

然后您可以在您的对象上调用 anyNonEmpty 以获得 getClassInfo 结果。

另请注意,我在 trait 中使用了 def,因为 trait 中的 val 由于初始化问题通常不是一个好主意。

如果你真的需要反射可以试试

import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._

def getClassInfo(myClass: MyClass): Boolean = {
  def fields[A: TypeTag] = typeOf[A].members.collect {
    case m: MethodSymbol if m.isGetter && m.isPublic => m
  }
  val mtFields = fields[MyTrait]
  val mcFields = fields[MyClass]
  val mtFieldNames = mtFields.map(_.name).toSet
  val mcNotMtFields = mcFields.filterNot(f => mtFieldNames.contains(f.name))
  val instanceMirror = currentMirror.reflect(myClass)
  val mcNotMtFieldValues = mcNotMtFields.map(f => instanceMirror.reflectField(f).get)
  mcNotMtFieldValues.exists(_.isInstanceOf[Some[_]])
}

val a = getClassInfo(MyClass()) //false
val b = getClassInfo(MyClass(Some("a"), Some("B"), Some("c"))) //true
val c = getClassInfo(MyClass(z = Some("z"))) //true
val d = getClassInfo(MyClass(x = Some("x"), y = Some("y")))//false