为什么 Scala 编译器不允许使用默认参数的重载方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4652095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does the Scala compiler disallow overloaded methods with default arguments?
提问by soc
While there might be valid cases where such method overloadings could become ambiguous, why does the compiler disallow code which is neither ambiguous at compile time nor at run time?
虽然可能存在这样的方法重载可能变得模棱两可的有效情况,但为什么编译器不允许在编译时和运行时既不模棱两可的代码呢?
Example:
例子:
// 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 ...
Are there any reasons why these restrictions can't be loosened a bit?
有什么理由不能放宽这些限制吗?
Especially when converting heavily overloaded Java code to Scala default arguments are a very important and it isn't nice to find out after replacing plenty of Java methods by one Scala methods that the spec/compiler imposes arbitrary restrictions.
特别是当将重载的 Java 代码转换为 Scala 默认参数非常重要时,在用一个 Scala 方法替换大量 Java 方法后发现规范/编译器强加了任意限制并不好。
采纳答案by Eugen Labun
I'd like to cite Lukas Rytz (from here):
我想引用 Lukas Rytz(从这里):
The reason is that we wanted a deterministic naming-scheme for the generated methods which return default arguments. If you write
def f(a: Int = 1)the compiler generates
def f$default$1 = 1If you have two overloads with defaults on the same parameter position, we would need a different naming scheme. But we want to keep the generated byte-code stable over multiple compiler runs.
原因是我们想要一个确定性的命名方案来生成返回默认参数的方法。如果你写
def f(a: Int = 1)编译器生成
def f$default$1 = 1如果在同一参数位置有两个具有默认值的重载,我们将需要不同的命名方案。但是我们希望在多次编译器运行中保持生成的字节码稳定。
A solution for future Scala version could be to incorporate type namesof the non-default arguments (those at the beginning of a method, which disambiguate overloaded versions) into the naming schema, e.g. in this case:
未来 Scala 版本的解决方案可能是将非默认参数的类型名称(在方法开头的那些,消除重载版本的歧义)合并到命名模式中,例如在这种情况下:
def foo(a: String)(b: Int = 42) = a + b
def foo(a: Int) (b: Int = 42) = a + b
it would be something like:
它会是这样的:
def foo$String$default = 42
def foo$Int$default = 42
Someone willing to write a SIP proposal?
有人愿意写 SIP 提案吗?
回答by Martin Odersky
It would be very hard to get a readable and precise spec for the interactions of overloading resolution with default arguments. Of course, for many individual cases, like the one presented here, it's easy to say what should happen. But that is not enough. We'd need a spec that decides all possible corner cases. Overloading resolution is already very hard to specify. Adding default arguments in the mix would make it harder still. That's why we have opted to separate the two.
对于重载解析与默认参数的交互,很难获得可读且精确的规范。当然,对于许多个别情况,就像这里介绍的情况一样,很容易说出应该发生什么。但这还不够。我们需要一个规范来决定所有可能的极端情况。重载分辨率已经很难指定。在混合中添加默认参数会使它变得更加困难。这就是我们选择将两者分开的原因。
回答by Landei
I can't answer your question, but here is a workaround:
我无法回答您的问题,但这里有一个解决方法:
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
}
If you have two very long arg lists which differ in only one arg, it might be worth the trouble...
如果您有两个非常长的 arg 列表,它们仅在一个 arg 中不同,那么麻烦可能值得...
回答by belka
What worked for me is to redefine (Java-style) the overloading methods.
对我有用的是重新定义(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"
This ensures the compiler what resolution you want according to the present parameters.
这可以确保编译器根据当前参数获得您想要的分辨率。
回答by Guillaume Massé
Here is a generalization of @Landei answer:
这是@Landei 答案的概括:
What you really want:
你真正想要的是:
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 ..."
回答by Shiva Wu
One of the possible scenario is
一种可能的情况是
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
The compiler will be confused about which one to call. In prevention of other possible dangers, the compiler would allow at most one overloaded method has default arguments.
编译器会对调用哪一个感到困惑。为了防止其他可能的危险,编译器将允许最多一个重载方法具有默认参数。
Just my guess:-)
只是我的猜测:-)
回答by Janx
My understanding is that there can be name collisions in the compiled classes with default argument values. I've seen something along these lines mentioned in several threads.
我的理解是在编译的类中可能会存在具有默认参数值的名称冲突。我在几个线程中看到了沿着这些方向的一些东西。
The named argument spec is here: http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf
命名参数规范在这里:http: //www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf
It states:
它指出:
Overloading If there are multiple overloaded alternatives of a method, at most one is
allowed to specify default arguments.
So, for the time being at any rate, it's not going to work.
所以,目前无论如何,它是行不通的。
You could do something like what you might do in Java, eg:
你可以做一些类似于你在 Java 中所做的事情,例如:
def foo(a: String)(b: Int) = a + (if (b > 0) b else 42)

