重载使将方法提升为函数变得有点困难:
object A {
def foo(a: Int) = 0
def foo(b: Boolean) = 0
def foo(a: Int, b: Int) = 0
val function = foo _ // fails, must use = foo(_, _) or (a: Int) => foo(a)
}
您无法选择性地导入一组重载方法中的其中一个。
当尝试应用隐式视图来调整参数以适应参数类型时,可能会更容易出现歧义:
scala> implicit def S2B(s: String) = !s.isEmpty
S2B: (s: String)Boolean
scala> implicit def S2I(s: String) = s.length
S2I: (s: String)Int
scala> object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") }
<console>:15: error: ambiguous reference to overloaded definition,
both method foo in object test of type (b: Boolean)Int
and method foo in object test of type (a: Int)Int
match argument types (java.lang.String)
object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") }
object test {
def foo(a: Int) = 0;
def foo(a: Int, b: Int = 0) = 1
}
scala> object O { def apply[T](ts: T*) = (); def apply(f: (String => Int)) = () }
defined object O
scala> O((i: String) => f(i)) // oops, I meant to call the second overload but someone changed the return type of `f` when I wasn't looking...
_.foo
问题的故障是Scala的有限类型推断,而不是重载。你回答了这个问题,但其中一些原因是由于Scala中其他弱点,这些弱点可以得到改进。重载比运行时下转换一个分离或名称的笛卡尔积更高效,或者说嘈杂且与共享语义脱节。 - Shelby Moore IIIaddIntToDouble
和addDoubleToInt
,那将会怎样。用名称替换类型似乎是倒退的。与其说Scala做得好,不如说Java做得更好。 - Shelby Moore III如果可能的话,避免重载是一个很好的选择,Gilad和Jason(retronym)所给出的原因都非常充分。Gilad的原因主要集中在为什么重载在一般情况下都是有问题的,而Jason的原因则更多地关注于它在其他Scala特性的背景下为何有问题。
在Jason的列表中,我还要补充一点,那就是重载与类型推断相互作用不佳。考虑以下代码:
val x = ...
foo(x)
对于 x
推断类型的更改可能会改变调用哪个 foo
方法。 x
的值不需要更改,只需更改推断类型,这可能发生因为各种各样的原因。
出于所有给出的原因(以及我肯定忘记的一些原因),我认为应尽可能少地使用方法重载。
我认为这个建议并不是特别针对Scala,而是针对面向对象编程(OO)的一般性建议(据我所知,Scala被认为是OO和函数式编程之间最好的结合)。
覆盖是可以接受的,它是多态的核心,也是OO设计的核心。
重载则更加棘手。使用方法重载时,很难确定哪个方法会被真正调用,这通常会导致混淆。而且很少有正当理由需要使用重载。大多数情况下,问题都可以通过其他方式解决,我同意重载是一种“坏味道”。
这里有一篇文章,很好地解释了我所说的“重载是混淆的根源”,我认为这也是为什么要避免使用重载的主要原因。虽然这篇文章是针对Java的,但我认为它同样适用于Scala。