在我的团队中,我经常看到队友写作
list.filter(_.isInstanceOf[T]).map(_.asInstanceOf[T])
但这对我来说似乎有点多余。
如果我们知道过滤列表中的所有内容都是T
的实例,那么为什么我们必须明确地将其转换为?
我知道一种替代方法,即使用match
。
例如:
list.match {
case thing: T => Some(thing)
case _ => None
}
但这有一个缺点,那就是我们必须明确说明一般情况。
所以,鉴于以上所述,我有两个问题:
1)还有另一种(更好吗?)方式做同样的事情?
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
的集合。否则,你犯的第一个错误,就是在运行时爆炸,这从来都不是很有趣。
我知道一种替代方法,即使用匹配。
由于类型擦除,您的示例代码无法正常工作。如果要匹配基础类型,则需要分别对每种情况使用ClassTag
和TypeTag
,并使用=:=
进行类型相等,使用<:<
进行子类型关系。< / p>
还有另一种(更好吗?)方式做同样的事情?
是的,使用类型系统工作,而不是反对它。尽可能使用类型集合。您还没有详细说明为什么您需要对类型使用运行时检查和强制转换,因此我假设对此有合理的解释。
如果没有,上述两个选项中的哪一个应该是首选的?
这是一种品味问题,但在类型上使用模式匹配可能更容易出错,因为必须意识到类型在运行时被擦除的事实,并为类型创建更多的样板代码你需要维护。