关于类型:Scala中的多态方法-为什么允许这样做?

Polymorphic Methods in Scala- why is this allowed?

我在scala中具有以下多态方法:

1
2
3
4
  def addTwoThings[S](item1:S, item2:S) =
  {
    item1 +" |" + item2
  }

尽管我已指定item1和item2应该具有相同的类型" S",但以下内容仍可以正常编译。我需要使用隐式证据来做些什么吗?

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

1
println(addTwoThings("1",2))


在没有显式使用.toString的情况下获取+运算符对您有用的原因在此进行了描述:什么Scala功能允许plus运算符在Any上使用? Predef中的那些附加隐式是scala中许多问题的根源,但是很难摆脱这种遗留问题。

要找出为什么addTwoThings("1",2)起作用的原因-让我们重写它以获得对S

的准确推断

1
2
3
4
5
scala> def addTwoThings[S](item1:S, item2:S): S = item1
addTwoThings: [S](item1: S, item2: S)S

scala> addTwoThings(1,"1")
res5: Any = 1

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

因此,这里有几种解决方案:

1)如果您可以在方法的签名中允许两个类型参数,请使用以下解决方案:

1
2
3
def addTwoThings[S1, S2](item1:S1, item2:S2)(implicit ev: S1 =:= S2, ev2: S2 =:= S1) = {
    item1 +" |" + item2
}

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

实验:

1
2
3
4
5
6
7
scala> addTwoThings(1,"1")
<console>:18: error: Cannot prove that Int =:= String.
              addTwoThings(1,"1")
                          ^

scala> addTwoThings("2","1")
res11: String = 2 | 1

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

1
2
3
4
5
6
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

实验:

1
2
3
4
5
6
7
8
9
10
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

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

注2:编译器提供的错误很难理解,更多信息在这里-使用无形类型不等式时如何自定义Scala模糊不清的隐式错误

3)另一种方法正在出现:

1
def addTwoThings[S](item1:S)(item2:S) =""

实验:

1
2
3
4
5
6
7
8
9
scala> addTwoThings(1)(1)
res8: String =""

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

类型推断不会寻找咖喱参数的普通超类型(您可以在此处阅读更多内容)


再一次,这就是Scala编译器的类型推断问题。

您必须给出明确的类型来指导编译器

1
addTwoThings[String]("1",2)

以上将给出编译错误。

您的代码起作用的原因

String和Int的常见超级类型是Any。因此,Scala编译器假定函数中的SAny

1
2
3
4
5
6
7
8
9
10
11
12
13
14
scala> def addTwoThings[S](item1:S, item2:S) =
     |   {
     |     item1 +" |" + item2
     |   }
addTwoThings: [S](item1: S, item2: S)String

scala> println(addTwoThings("1",2))
1 | 2

scala> println(addTwoThings[String]("1",2))
<console>:22: error: type mismatch;
 found   : Int(2)
 required: String
       println(addTwoThings[String]("1",2))