虽然可能存在这样的方法重载可能变得模糊的有效情况,但为什么编译器不允许在编译时和运行时都不明确的代码?
示例:
// This fails:
def foo(a: String)(b: Int = 42) = a + b
def foo(a: Int) (b: Int = 42) = a + b
// This fails, too. Even if there is no position in the argument list,
// where the types are the same.
def foo(a: Int) (b: Int = 42) = a + b
def foo(a: String)(b: String = "Foo") = a + b
// This is OK:
def foo(a: String)(b: Int) = a + b
def foo(a: Int) (b: Int = 42) = a + b
// Even this is OK.
def foo(a: Int)(b: Int) = a + b
def foo(a: Int)(b: String = "Foo") = a + b
val bar = foo(42)_ // This complains obviously ...
有什么理由说明为什么这些限制不能放松一点?
特别是在将重载过多的Java代码转换为Scala时,默认参数是非常重要的,在使用一个Scala方法替换大量Java方法后发现规范/编译器强加任意限制是不好的。
答案 0 :(得分:106)
我想引用Lukas Rytz(来自here):
原因是我们想要一个确定性的命名方案 生成的方法返回默认参数。如果你写
def f(a: Int = 1)
编译器生成
def f$default$1 = 1
如果在同一参数上有两个默认值的重载 位置,我们需要一个不同的命名方案。但我们想保留 生成的字节码在多个编译器运行时保持稳定。
未来Scala版本的解决方案可以是将非默认参数的类型名称(在方法开头的那些,消除了重载版本的歧义)纳入命名模式,例如:在这种情况下:
def foo(a: String)(b: Int = 42) = a + b
def foo(a: Int) (b: Int = 42) = a + b
它会是这样的:
def foo$String$default$2 = 42
def foo$Int$default$2 = 42
有人愿意write a SIP proposal?
答案 1 :(得分:66)
对于重载分辨率与默认参数的交互,获取可读且精确的规范将非常困难。当然,对于许多个别情况,例如这里介绍的情况,很容易说出应该发生什么。但这还不够。我们需要一个决定所有可能的极端情况的规范。重载分辨率已经非常难以指定。在混合中添加默认参数会使其更难。这就是我们选择将两者分开的原因。
答案 2 :(得分:10)
我无法回答你的问题,但这是一个解决方法:
implicit def left2Either[A,B](a:A):Either[A,B] = Left(a)
implicit def right2Either[A,B](b:B):Either[A,B] = Right(b)
def foo(a: Either[Int, String], b: Int = 42) = a match {
case Left(i) => i + b
case Right(s) => s + b
}
如果你有两个非常长的arg列表,只有一个arg不同,那可能值得这么麻烦......
答案 3 :(得分:4)
对我来说有用的是重新定义(Java风格)重载方法。
def foo(a: Int, b: Int) = a + b
def foo(a: Int, b: String) = a + b
def foo(a: Int) = a + "42"
def foo(a: String) = a + "42"
这可以确保编译器根据当前参数获得所需的分辨率。
答案 4 :(得分:1)
可能的情况之一是
def foo(a: Int)(b: Int = 10)(c: String = "10") = a + b + c
def foo(a: Int)(b: String = "10")(c: Int = 10) = a + b + c
编译器会对要调用哪一个感到困惑。在防止其他可能的危险时,编译器最多允许一个重载方法具有默认参数。
我的猜测: - )
答案 5 :(得分:1)
以下是@Landei答案的概括:
你真正想要的是什么:
def pretty(tree: Tree, showFields: Boolean = false): String = // ...
def pretty(tree: List[Tree], showFields: Boolean = false): String = // ...
def pretty(tree: Option[Tree], showFields: Boolean = false): String = // ...
Workarround
def pretty(input: CanPretty, showFields: Boolean = false): String = {
input match {
case TreeCanPretty(tree) => prettyTree(tree, showFields)
case ListTreeCanPretty(tree) => prettyList(tree, showFields)
case OptionTreeCanPretty(tree) => prettyOption(tree, showFields)
}
}
sealed trait CanPretty
case class TreeCanPretty(tree: Tree) extends CanPretty
case class ListTreeCanPretty(tree: List[Tree]) extends CanPretty
case class OptionTreeCanPretty(tree: Option[Tree]) extends CanPretty
import scala.language.implicitConversions
implicit def treeCanPretty(tree: Tree): CanPretty = TreeCanPretty(tree)
implicit def listTreeCanPretty(tree: List[Tree]): CanPretty = ListTreeCanPretty(tree)
implicit def optionTreeCanPretty(tree: Option[Tree]): CanPretty = OptionTreeCanPretty(tree)
private def prettyTree(tree: Tree, showFields: Boolean): String = "fun ..."
private def prettyList(tree: List[Tree], showFields: Boolean): String = "fun ..."
private def prettyOption(tree: Option[Tree], showFields: Boolean): String = "fun ..."
答案 6 :(得分:0)
我的理解是在编译的类中可能存在名称冲突,并带有默认参数值。我已经看到了几个主题中提到的这些内容。
命名参数规范如下: http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf
它声明:
Overloading If there are multiple overloaded alternatives of a method, at most one is
allowed to specify default arguments.
所以,暂时无论如何,它都无法发挥作用。
你可以做类似于Java的事情,例如:
def foo(a: String)(b: Int) = a + (if (b > 0) b else 42)