通用的强类型scala方法,用于从集合中检索特定类型的项



假设我有这样一个类:

class Example {
    val list = List(new Apple(), new Orange(), Banana());
    def getIfPresent[T <: Fruit] : Option[T] = list.collectFirst { case x : T => x }
}

你可以这样使用:

val example = new Example();
match example.getIfPresent[Apple] {
    case Some(apple) => apple.someAppleSpecificMethod();
    case None => println("No apple");
}
现在,当然,这在JVM中不起作用,因为类型擦除。getIfPresent只匹配collectFirst部分函数中的Fruit类型,而不是调用中指定的实际类型。

我试着让我的头围绕类型标签和类标签,真的不知道我将如何实现上述方法。我看到的例子都在尝试做非常不同的事情。我怎么能实现一个方法,做我想要的,无论是与TypeTags或其他一些机制,我不知道?


编辑:m-z的答案下面是完整的解决方案,但这里是它看起来与我的示例代码:

class Example {
    val list = List(new Apple(), new Orange(), Banana());
    def getIfPresent[T <: Fruit : ClassTag] : Option[T] = list.collectFirst { case x : T => x }
}

只需要添加: ClassTag !

您可以使用ClassTag在某种程度上做到这一点。

import scala.reflect.ClassTag
// Modify to apply whatever type bounds you find necessary
// Requires Scala ~2.11.5 or greater (not sure of the exact version, but 2.11.1 does not work, and 2.11.5 does)
def findFirst[A : ClassTag](list: List[Any]): Option[A] =
    list collectFirst { case a: A => a }
val l = List(1, "a", false, List(1, 2, 3), List("a", "b"))
scala> findFirst[Boolean](l)
res22: Option[Boolean] = Some(false)
scala> findFirst[Long](l)
res23: Option[Long] = None

但是ClassTag有一些注意事项,因为它只匹配类,而不匹配类型:

scala> findFirst[List[String]](l)
res24: Option[List[String]] = Some(List(1, 2, 3)) // No!

你可以使用TypeTag来解决这个问题,但它不能与List[Any]一起工作。这里有一个可能的(有点难看的)技巧:

import scala.reflect.runtime.universe.{typeOf, TypeTag}
case class Tagged[A : TypeTag](a: A) {
    def tpe = typeOf[A]
}
implicit class AnyTagged[A : TypeTag](a: A) {
    def tag = Tagged(a)
}
def findFirst[A : TypeTag](list: List[Tagged[_]]): Option[A] =
    list collectFirst { case tag @ Tagged(a) if(tag.tpe =:= typeOf[A]) => a.asInstanceOf[A] }

我能想到的保存每个元素的TypeTag的唯一方法是用包装器类来保存它。所以我必须像这样构造这个列表:

val l = List(1.tag, "a".tag, false.tag, List(1, 2, 3).tag, List("a", "b").tag)

但是它有效:

scala> findFirst[List[String]](l)
res26: Option[List[String]] = Some(List(a, b))

可能有一种更优雅的方法使用TypeTag s来构造这样的列表。


为了好玩,您也可以尝试使用HListselect来完成shapeless。不同之处在于,select将返回A(您想要的类型),而不是返回Option[A],但是如果HList不包含A它将不会编译

import shapeless._
val l = 1 :: "a" :: false :: List(1, 2, 3) :: List("a", "b") :: HNil
scala> l.select[Boolean]
res0: Boolean = false
scala> l.select[Boolean]
res1: Boolean = false
scala> l.select[List[String]]
res2: List[String] = List(a, b)
scala> l.select[Long]
<console>:12: error: Implicit not found: shapeless.Ops.Selector[shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.::[List[Int],shapeless.::[List[String],shapeless.HNil]]]]], Long]. You requested an element of type Long, but there is none in the HList shapeless.::[Int,shapeless.::[String,shapeless.::[Boolean,shapeless.::[List[Int],shapeless.::[List[String],shapeless.HNil]]]]].
              l.select[Long]
                      ^

相关内容

  • 没有找到相关文章

最新更新