scala - 如何测试基类型对象集合中是否存在一组类型?

标签 scala

我需要将传入对象的类型与一组预定义对象相交。

原始方法是为每个预定义类型扫描传入集合:

trait Field
class Field1 extends Field
class Field2 extends Field
class Field3 extends Field
...

class FieldManager(shownFields:Iterable[Field]) {
  var hiddenFields = new ArrayBuffer[Field]
  var found = false
  for (sf <- shownFields) {
    if (sf.isInstanceOf[Field1]) {
      found = true
      break
    }
  if (!found)
    hiddenFields+=new Field1
  for (sf <- shownFields) {
    if (sf.isInstanceOf[Field2]) {
      found = true
      break
    }
  if (!found)
    hiddenFields+=new Field2
  for (sf <- shownFields) {
    if (sf.isInstanceOf[Field3]) {
      found = true
      break
    }
  if (!found)
    hiddenFields+=new Field3
  ...
}

哇,这对 Scala 来说太冗长了!应该有更短的方法。类似于 C++ 中的函数模板:

class FieldManager {
  vector<Field*> hiddenFields, shownFields;
  template<class T>
  void fillHiddenType() {
    FOR_EACH(Field *, field, shownFields) {
      if (dynamic_cast<T*>(field))
        return
      hiddenFields.push_back(new T)
    }
  }
  void fillHidden() {
    fillHiddenType<Field1>();
    fillHiddenType<Field2>();
    fillHiddenType<Field3>();
    ...
  }
};

在这个 C++ 示例中,我们提到每种类型只扫描一次。

好的,Scala 的类型参数也很好,让我们试试:

def fillHiddenType[T] {
  for (sf <- shownFields)
    if (sf.isInstanceOf[T])
      return
  hiddenFields+=new T  
}

但是编译器不喜欢创建 T 实例:需要类类型但找到了 T。尝试将实例作为参数传递,出现下一个问题 warning: abstract type T in type is unchecked since it is eliminated by erasure and isInstanceOf[] 始终为真!类型 T 抽象得非常好,甚至 [T <: Field] 也不允许将其与我们的集合 shownFields 中的类型进行比较。

问题是: 如何以紧凑的方式测试集合中给定类型的对象是否存在?

更新 以下工作代码在 Scala 2.7.7 上进行了测试,这两个答案都很有用。谢谢你们!

//Scala 2.7.7 misses toSet members
implicit def toSet[T](i:Iterable[T]): Set[T] = {
    val rv = new HashSet[T]
    rv ++= i
    rv
}

def filterOut[T](all:Iterable[T], toRemove:Set[T]) = {
    all.filter(x => ! toRemove.contains(x))
}

def filterOutByType[T <: AnyRef](all:Iterable[T], toFilter:Set[T]):Iterable[T] = {
    def toClass(x:AnyRef) = x.getClass.asInstanceOf[Class[T]]
    val allTypes = all map toClass
    val extraTypes = toSet(filterOut(allTypes, toFilter map toClass))
    all.filter(extraTypes contains toClass(_))
}

最佳答案

找到具有给定类型的集合的第一个成员是微不足道的:

shownFields.find(_.isInstanceOf[Field1])

但这仍然会返回 Option[Field] 的实例,而不是 Option[Field1] - 您需要强类型化。 collect 方法将在此处提供帮助:

showFields.collect{case x : Field1 => x}

这将返回一个 Iterable[Field1],然后您可以使用 headOption 选择可迭代对象的第一个元素作为 Option[Field1],或者 Some[Field1] 如果存在,或者 None 否则:

showFields.collect{case x : Field1 => x}.headOption

为了提高效率,而不是计算列表中的所有 Field1,我还通过 view 方法使其变得惰性:

showFields.view.collect{case x : Field1 => x}.headOption

然后如果未找到默认值,请使用 Options 提供的 getOrElse 方法:

showFields.view.collect{case x : Field1 => x}.headOption getOrElse (new Field1)

更新

我刚刚回顾了这个问题,如果您似乎希望 hiddenFields 包含 showFields 中没有成员的每个 Field 子类型的新实例。

查找 Iterable 中表示的所有类型:

val shownFieldTypes = showFields.map(_.getClass).toSet

(将其转换为集合强制使用唯一值)

如果您有一组您感兴趣的字段:

val allFieldTypes = Set(classOf[Field1], classOf[Field2], ...)

你可以减去找到缺失的:

val hiddenFieldTypes = allFieldTypes -- shownFieldTypes

这里的问题是你会被困在使用 newInstance 中,反射并不总是可取的......所以:

val protoHiddenFields = Set(new Field1, new Field2, ...)
val allFieldTypes = protoHiddenFields.map(_.getClass)
val hiddenFieldTypes = allFieldTypes -- shownFieldTypes
val hiddenFields = protohiddenFields.filter(hiddenFieldTypes contains _.getClass)

这种方法的美妙之处在于,如果您愿意,可以使用构造函数参数初始化您的原型(prototype)

关于scala - 如何测试基类型对象集合中是否存在一组类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3995364/

相关文章:

scala - 为什么 Scala 的 == 在 Int 的情况下不同?

scala - 如何从任务中打印累加器变量(似乎为 "work"而没有调用 value 方法)?

scala - 如何在Spark中的执行器之间同步功能以避免在写入Elastic时并发

scala - Eclipse scala "Could not find or load main class"

Scala单方法接口(interface)实现

scala - 使用Spark Databricks平台从URL读取数据

sql - 将数据帧添加到 Spark 中的列表

java - ZMQ 丢失事件在 jeromq scala 中传播

xml - 隐式 append Scala XML 文字

scala - 如何为具有默认参数的案例类编写 Scala 提取器?