我正在查看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#foo
是y
类型,我希望对Child
进行评估。传递bar
1到foo
意味着baz
的默认值为3.所以它应该产生4.但我的理解当然是不正确的。
scala> y.foo(bar = 1)
res7: Int = 5
答案 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
编译后没有命名参数 - 所有参数都是位置参数。
因为bar
是Child#foo
此代码的第二个参数:
c.foo(bar = 1)
// Int = 4
由编译器翻译成:
c.foo(c.`foo$default$1`, /*bar = */1)
// Int = 4
但由于bar
是Parent#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.