Scala 2.8新特性:Default Argument

这里就不翻译Default Arguments的意义了,因为我觉得很难找到一个合适的词来准确的描述它。

Scala 2.8中提供了Default Arguments这个新的特性,终于可以高兴的说:不用再使用implicit argument来做Default Argument了。

先来看看Scala 2.8中Default Argument的使用:

def f(a: Int, b: String = "defaultString", c: Int = 5)

f(1)
f(1, "otherString")
f(1, c = 10) // 需要用具名参数来指定c

例子非常简单,但是能够很好的说明Default Argument是如何使用的。这里需要注意一下,除了末尾的参数,如果想跳过任何中间的参数都需要使用具名参数。

在我们使用这个新的特性的时候,Scala 2.8会为每一个Default Argument生成一个方法,比如:

def f(a: Int = 1, b: String)
// generates a method: def f$default$1 = 1

f(b = "3")
// transformed to: f(b = "3", a = f$default$1)

当我们在调用方法时使用了Default Argument特性,Scala 2.8就会将生成的方法作为缺失的参数加入到调用的参数列表中去。

Default Argument可以是任意的表达式。因为参数的有效范围包括所有的子参数列表和方法体,我们甚至可以在子参数中使用前一个参数列表中的参数来作为Default Argument。例如:

def f(a: Int = 0)(b: Int = a + 1) = b // OK
// def f(a: Int = 0, b: Int = a + 1) // 错误,找不到变量a

f(10)() // returns 11 (not 1)

这里注意到的是,作为Default Argument的参数不能是同一个参数列表中定义的参数,只能是前面参数列表中定义的参数。

接下来是有关泛型的Default Argument的使用。当我们使用 "x:T = expr" 这样的参数时,Scala的类型检查会去寻找最匹配的类型来赋予T:

def f[T](a: T = 1) = a

f() // returns 1: Int
f("s") // returns "s": String

def g[T](a: T = 1, b: T = "2") = b

g(a = "1") // OK, returns "2": String
g(b = 2) // OK, returns 2: Int
g() // OK, returns "2": Any
// g[Int]() // "error: type mismatch; found: String, required: Int"

class A[T](a: T = "defaultString")
new A() // creates an instance of A[String]

从上面可以看出,不管是普通的方法还是构造函数,Scala都会为Default Argument来寻找最合适的类型。
 

Default Argument和其它特性集成

  • By-Name Parameters
Default Argument和By-Name Parameters没有任何的冲突。不过在使用时需要知道的是,如果我们在调用方法时忽略了By-Name的参数,那么Scala会在每次调用这个方法时执行一次By-Name参数的Default Argument。

  • Repeated Parameters
如果你使用了Repeated Parameters,那么很不幸,Scala不允许你为Repeated Parameters指定任何的Default Argument。

  • Overriding
但我们在子类中覆盖或者实现超类的方法时,Default Argument也会被继承或者覆盖掉,例如:

trait A { def f(a: Int = 1, b: Int): (Int, Int) }

// B: inherit & add a default
class B extends A { def f(a: Int, b: Int = 2) = (a, b) }

// C: override a default
class C extends A { def f(a: Int = 3, b: Int ) = (a, b) }

val a1 = new B
val a2 = new C

a1.f() // return (1,2)
a2.f(b=2) // return (3, 2)

可以看到class B从trait A中实现了f方法,并且对b参数增加了Default Argument,虽然参数a并没有指定Default Argument,但是它可以从trait A中继承;而class C实现了f方法且重写了参数a的Default Argument。

另外,在类型检查时会使用静态类型来检测一个方法的参数是否含有Default Argument,所以下面的用法是不行的:

val a1: A = new B
// a1.f() // "error: unspecified parameter: value b"

  • Overloading
关于重载方法还是遵循一个原则,就是如果存在多个重载的方法,最终会选择最匹配的那个。那么如果有下面的情况会怎么样呢?

def f(a: Object) // #1
def f(a: String, b: Int = 1) // #2

f("str")

在调用f("str")的时候,我们会发现#1和#2两个方法都合适,此时按照原则来说,#1显然最合适,所以此时#1方法会被执行。

  • Case Classes
为什么会提及Case Class呢?因为现在Scala 2.8会为每一个case class生成一个名为copy的方法。这个方法的作用顾名思义,就是用来得到这个class的副本。这个方法的参数来自于class的主构造函数,并且它们的Default Argument就是主构造函数的参数。也就是说,如果现在你的case class或者是它的超类中没有定义过copy方法的话,那么Scala就会生成一个。如:

case class A[T](a: T, b: Int) {
  // def copy[T’](a’: T’ = a, b’: Int = b): A[T’] =
  // new A[T’](a’, b’)
}
val a1: A[Int] = A(1, 2)
val a2: A[String] = a1.copy(a = "someString")

如果一个case class继承于另外一个case class的话,很明显,只有父类中会存在copy方法,子类只会继承而不会重新生成它。

  • Implicit Parameters
額。。。我承认原先是使用Implicit Parameters来实现Default Argument的,但是现在这两个特性可以结合使用了。当我们忘记了指定Implicit Values的时候,这些Default Argument会起作用的。

def f(implicit a: String = "value", y: Int = 0) = a +": "+ y
println(f) // prints "value: 0"

implicit val s = "size"
println(f) // prints "size: 0"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值