Scala命名为参数

时间:2013-12-18 03:06:32

标签: scala

我正在查看Scala in Depth中的Named Arguments示例:

scala> class Parent {
     | def foo(bar: Int = 1, baz: Int = 2): Int = bar + baz
     | }
defined class Parent

scala> class Child extends Parent {
     |    override def foo(baz: Int = 3, bar: Int = 4): Int = super.foo(baz, bar)
     | }
defined class Child

scala> val p = new Parent
p: Parent = Parent@6100756c

scala> p.foo()
res1: Int = 3

scala> val x = new Child
x: Child = Child@70605759

调用x.foo()的计算结果为7,因为Child#foo的默认参数为3和4。

scala> x.foo()
res3: Int = 7

在运行时实例化新的Child,但在编译时实例化Parent这可能是也可能不正确

scala> val y: Parent = new Child
y: Parent = Child@540b6fd1

调用x.foo()的计算结果为7,因为Child#foo的默认参数为3和4。

scala> y.foo()
res5: Int = 7

调用x.foo()评估为4,因为Child#foo的默认baz参数为3。

scala> x.foo(bar = 1)
res6: Int = 4

但是,我不明白为什么y.foo(bar = 1)会返回5.由于Child#fooy类型,我希望对Child进行评估。传递bar 1到foo意味着baz的默认值为3.所以它应该产生4.但我的理解当然是不正确的。

scala> y.foo(bar = 1)
res7: Int = 5

1 个答案:

答案 0 :(得分:7)

有两个原因:

默认参数实现

scala编译器为默认参数创建辅助方法:

val p = new Parent()
val c = new Child()

p.`foo$default$1`
// Int = 1
p.`foo$default$2`
// Int = 2

c.`foo$default$1`
// Int = 3
c.`foo$default$2`
// Int = 4

这就是为什么你不仅可以使用常量,还可以使用默认参数的字段和方法:

def test(i: Int = util.Random.nextInt) = i

test()
// Int = -1102682999

test()
// Int = -1994652923

命名参数实现

编译后没有命名参数 - 所有参数都是位置参数。

因为barChild#foo此代码的第二个参数:

c.foo(bar = 1)
// Int = 4

由编译器翻译成:

c.foo(c.`foo$default$1`, /*bar = */1)
// Int = 4

但由于barParent#foo此代码的第一个参数:

val tmp: Parent = c
tmp.foo(bar = 1)
// Int = 5

被翻译成:

tmp.foo(/*bar = */1, tmp.`foo$default$2`)
// Int = 5

我们已经知道c.foo$default$2会返回4,因此c.foo(1, 4)会返回5.