Scala:isInstanceOf后跟asInstanceOf

4
在我的团队中,我经常看到我的队友在写代码时...
list.filter(_.isInstanceOf[T]).map(_.asInstanceOf[T])

但是这对我来说似乎有些冗余。

如果我们知道过滤列表中的每个内容都是 T 的实例,那么为什么还需要将其显式转换为这样的呢?


我知道另一种替代方法,那就是使用match

例如:

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

但这种方法的缺点是我们必须明确指出一般情况。
所以,基于上述所有内容,我有两个问题:
1)是否有另一种(更好的)方法来完成同样的事情?
2)如果没有,应该优先考虑上述两个选项中的哪一个?
2个回答

10
您可以使用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,因此不应该提供通用的情况。

但如果需要,您可以使用 Function.unlift 将函数 A => Option[B] 转换为 PartialFunction[A, B]。以下是一个示例,还使用了 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))

2

但是对我来说,这似乎有点冗余。

也许你团队中的程序员正在试图保护那段代码,防止有人错误地插入类型不是 T 的情况,假设这是某种具有类型 Any 的集合。否则,您犯的第一个错误,将会在运行时出错,这并不好玩。

我知道一种替代方法,那就是使用匹配。

您的示例代码将无法正常工作,因为存在类型擦除。如果您想匹配基础类型,则需要分别为每个情况使用 ClassTagTypeTag,并使用 =:= 进行类型相等和 <:< 用于子类型关系。

是否有另一种(更好的?)方法来完成同样的事情?

是的,要与类型系统协作,而不是反对它。尽可能地使用带类型的集合。由于您没有详细说明需要在类型上运行时检查和强制转换的原因,因此我假设有一个合理的解释。

如果没有,上述两种选项中应该选择哪一种?

这是一个品味问题,但是在类型上进行模式匹配可能会更容易出错,因为必须了解在运行时类型被擦除的事实,并创建更多的样板代码供您维护。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接