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

时间:2016-10-15 07:10:14

标签: scala types polymorphism type-inference

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

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

如果我已经指定item1和item2应该是相同类型" S"以下,则编译很好。我是否需要用隐含证据做些什么?

要说清楚,我实际上希望编译器抱怨他们不是同一类型,但它似乎允许我继续,这让我很困惑。感谢。

println(addTwoThings("1",2))

2 个答案:

答案 0 :(得分:3)

此处描述了+运算符在不使用.toString的情况下为您工作的原因:What Scala feature allows the plus operator to be used on 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

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

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

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

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

注意:ev2可能是多余的,但它提供了更完整的平等,请参阅Scala: generic method using implicit evidence doesn't compile

实验:

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

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

2)或者您可以使用Evidence that types are not equal in 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

实验:

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) - 仍然有效。它仅保护您不受不相关的类型层次结构(可能有用)。实际上!= Any != AnyRef object A; object B; addTwoThings(B, A)不会在def addTwoThings[S](item1:S)(item2:S) = "" 上给您错误。

注2:编译器提供的错误几乎无法读取,更多信息请参见 - How can I customize Scala ambiguous implicit errors when using shapeless type inequalities

3)另一种方法是讨论:

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

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

实验:

<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
  <PropertyGroup>
    <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
  </PropertyGroup>
  <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>

类型推断不会查找curried参数的常见超类型(您可以阅读更多here

答案 1 :(得分:0)

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

您必须提供显式类型来指导编译器

addTwoThings[String]("1",2)

以上将给出编译错误。

代码工作的原因

String和Int的常见超类型是Any。因此,Scala编译器在S

的函数中假定Any
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))