简短回答您的问题:发生这种情况是因为您在尝试比较 function (String => Boolean) 与 option value 时使用了错误的 Option contains 方法这是String。
但是Option 中的contains 方法存在一些困难。
我们看一下scala源码中contains的定义:
/**@example {{{
* // Returns true because Some instance contains string "something" which equals "something".
* Some("something") contains "something"
*
* // Returns false because "something" != "anything".
* Some("something") contains "anything"
*
* // Returns false when method called on None.
* None contains "anything"
* }}}
*
* @param elem the element to test.
* @return `true` if the option has an element that is equal (as
* determined by `==`) to `elem`, `false` otherwise.
*/
final def contains[A1 >: A](elem: A1): Boolean =
!isEmpty && this.get == elem
我们看到contains 将包含Option[A] 的值与一些传递的类型为A1 的元素进行比较,该元素具有A 作为下限[A1 >: A]。这意味着类型参数A1 或抽象类型A1 引用类型A 的超类型。这意味着我们可以传递String 类型的任何超类型来检查contains,例如Any。
在您的情况下,您试图传递到 contains function String => Boolean 但请查看 Function1 定义:
trait Function1[@specialized(scala.Int, scala.Long, scala.Float, scala.Double) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double) +R] extends AnyRef
这是一个扩展AnyRef(和Any)的特征。所以对于编译器来说,这是一个合法的案例,它检查String 是Any 的子类型并编译代码。在运行时,它会尝试比较 String 值和 function String => Boolean (String != String => Boolean),这就是 contains 总是返回 false 并过滤为空的原因列表。
所以要解决你的主要问题 - 使用地图正确过滤列表,最好使用exists函数:
val people = Seq(Person("Ned", Some("d")), Person("Alex", None))
val suspects = Map("c" -> 1, "d" -> 2)
val resultExist = people.filter { p =>
p.nickName.exists {
suspects.contains
}
}
val resultExistUnsugar = people.filter { p =>
p.nickName.exists {
n => suspects.contains(n)
}
}
println(resultExist) // List(Person(Ned,Some(d)))
println(resultExistUnsugar) // List(Person(Ned,Some(d)))
为了保护代码的类型安全,我建议您在使用contains 方法时设置具体的类型参数:
// it will not compile because suspects.contains type is not String
val result1 = people.filter(_.nickName.contains[String](suspects.contains))
// it will not compile also by the same reason
val result2 = people.filter { p =>
p.nickName.contains[String] {
n => suspects.contains(n)
}
}
但我们仍然不知道为什么contains 有类型参数边界[A1 >: A]。为了澄清这一点,让我们看一下Option[+A] 类定义:
sealed abstract class Option[+A] extends Product with Serializable { // ... }
这里我们看到Option 具有协方差类型参数+A,这就是我们应该在contains 方法中添加参数边界的原因。在this thread 中查看更多信息。
总结:
- 小心在所有容器中使用
contains 方法,而不仅仅是在Option
- 最好绑定一些可以进行类型参数化的方法,以实现类型安全和更可预测的行为
- 更多关于docs.scala-lang的下界和协方差