Scala:isInstanceOf后跟asInstanceOf

时间:2016-12-16 10:55:47

标签: scala

在我的团队中,我经常看到队友写作

list.filter(_.isInstanceOf[T]).map(_.asInstanceOf[T])

但这对我来说似乎有点多余。

如果我们知道过滤列表中的所有内容都是T的实例,那么为什么我们必须明确地将其转换为?

我知道一种替代方法,即使用match

例如:

list.match {
  case thing: T => Some(thing)
  case _ => None
}

但这有一个缺点,那就是我们必须明确说明一般情况。

所以,鉴于以上所述,我有两个问题:

1)还有另一种(更好吗?)方式做同样的事情?

2)如果没有,上述两个选项中的哪一个应该是首选的?

2 个答案:

答案 0 :(得分:7)

您可以使用collect

list collect {
  case el: T => el
}

真正的类型只是工作(当然禁止类型擦除):

scala> List(10, "foo", true) collect { case el: Int => el } 
res5: List[Int] = List(10)

但是,正如@YuvalItzchakov所提到的,如果你想匹配抽象类型T,你必须在范围内有一个隐含的ClassTag[T]

因此实现此功能的函数可能如下所示:

import scala.reflect.ClassTag

def filter[T: ClassTag](list: List[Any]): List[T] = list collect {
  case el: T => el
} 

使用它:

scala> filter[Int](List(1, "foo", true))
res6: List[Int] = List(1)

scala> filter[String](List(1, "foo", true))
res7: List[String] = List(foo)

collect需要PartialFunction,因此您不应提供通用案例。

但如果需要,您可以使用A => Option[B]将函数PartialFunction[A, B]转换为Function.unlift。下面是一个示例,也使用shapeless.Typeable来解决类型擦除问题:

import shapeless.Typeable
import shapeless.syntax.typeable._

def filter[T: Typeable](list: List[Any]): List[T] = 
  list collect Function.unlift(_.cast[T])

使用:

scala> filter[Option[Int]](List(Some(10), Some("foo"), true))
res9: List[Option[Int]] = List(Some(10))

答案 1 :(得分:2)

  

但这对我来说似乎有点多余。

也许你团队中的程序员试图屏蔽那段错误地插入T以外的类型的代码,假设这是某种类型为Any的集合。否则,你犯的第一个错误,就是在运行时爆炸,这从来都不是很有趣。

  

我知道一种替代方法,即使用匹配。

由于类型擦除,您的示例代码无法正常工作。如果要匹配基础类型,则需要分别对每种情况使用ClassTagTypeTag,并使用=:=进行类型相等,使用<:<进行子类型关系。< / p>

  

还有另一种(更好吗?)方式做同样的事情?

是的,使用类型系统工作,而不是反对它。尽可能使用类型集合。您还没有详细说明为什么您需要对类型使用运行时检查和强制转换,因此我假设对此有合理的解释。

  

如果没有,上述两个选项中的哪一个应该是首选的?

这是一种品味问题,但在类型上使用模式匹配可能更容易出错,因为必须意识到类型在运行时被擦除的事实,并为类型创建更多的样板代码你需要维护。