Scala 中的多态方法 - 为什么允许这样做?

Nov*_*d88 4 polymorphism types scala type-inference

我在 Scala 中有以下多态方法:

  def addTwoThings[S](item1:S, item2:S) =
  {
    item1 + " | " + item2
  }
Run Code Online (Sandbox Code Playgroud)

然而,以下编译得很好,即使我已经指定 item1 和 item2 应该是相同的类型“S”。我需要用隐性证据做些什么吗?

需要明确的是,我实际上希望编译器抱怨它们不是同一类型,但它似乎允许我继续,这让我感到困惑。谢谢。

println(addTwoThings("1",2))
Run Code Online (Sandbox Code Playgroud)

dk1*_*k14 5

此处描述了get+运算符在不.toString显式使用的情况下为您工作的原因:什么 Scala 功能允许在 Any 上使用加号运算符?. Predef在 scala 中那些额外的隐含是许多问题的根源,但很难摆脱这些遗留问题。

要找出为什么addTwoThings("1",2)有效 - 让我们重写它以获得准确的推断S

scala> def addTwoThings[S](item1:S, item2:S): S = item1
addTwoThings: [S](item1: S, item2: S)S

scala> addTwoThings(1, "1")
res5: Any = 1
Run Code Online (Sandbox Code Playgroud)

您可以注意到该类型S = Any被推断为常见类型。

所以,这里有几个解决方案:

1) 如果你可以在你的方法签名中允许两个类型参数,这里是解决方案:

def addTwoThings[S1, S2](item1:S1, item2:S2)(implicit ev: S1 =:= S2, ev2: S2 =:= S1) = {
    item1 + " | " + item2
}
Run Code Online (Sandbox Code Playgroud)

注意:ev2 对于检查来说可能是多余的,但它提供了更完整的相等性,请参阅Scala:使用隐式证据的泛型方法无法编译

实验:

scala> addTwoThings(1, "1")
<console>:18: error: Cannot prove that Int =:= String.
              addTwoThings(1, "1")
                          ^

scala> addTwoThings("2", "1")
res11: String = 2 | 1
Run Code Online (Sandbox Code Playgroud)

2) 或者您可以使用Scala 中类型不相等的证据排除Any/ AnyRef(所有事物的通用超类型):

trait =:!=[A, B]
implicit def neq[A, B] : A =:!= B = new =:!=[A, B] {}
implicit def neqAmbig1[A] : A =:!= A = ???
implicit def neqAmbig2[A] : A =:!= A = ???

def addTwoThings[S](item1:S, item2:S)(implicit ev: S =:!= Any, ev2: S =:!= AnyRef): S = item1
Run Code Online (Sandbox Code Playgroud)

实验:

scala> addTwoThings(1, "1")
<console>:18: error: ambiguous implicit values:
 both method neqAmbig1 of type [A]=> =:!=[A,A]
 and method neqAmbig2 of type [A]=> =:!=[A,A]
 match expected type =:!=[Any,Any]
              addTwoThings(1, "1")
                          ^

scala> addTwoThings(1, 1)
res7: Int = 1
Run Code Online (Sandbox Code Playgroud)

注意:这种方法不需要精确的类型相等,所以 if B1 <: B2- addTwoThings(b1, b2)- 仍然有效。它仅保护您免受不相关的类型层次结构的影响(这可能很有用)。在实践中!= Any没有!= AnyRef不会给你错误object A; object B; addTwoThings(B, A)

注2:编译器提供的错误几乎不可读,更多信息在这里 -如何在使用无形状类型不等式时自定义 Scala 模棱两可的隐式错误


3)另一种方法是柯里化:

def addTwoThings[S](item1:S)(item2:S) = ""
Run Code Online (Sandbox Code Playgroud)

实验:

scala> addTwoThings(1)(1)
res8: String = ""

scala> addTwoThings(1)("1")
<console>:18: error: type mismatch;
 found   : String("1")
 required: Int
              addTwoThings(1)("1")
                              ^
Run Code Online (Sandbox Code Playgroud)

类型推断不会寻找柯里化参数的常见超类型(您可以在此处阅读更多内容)