一、前言
前面学习了Scala的Class,下面接着学习Method(方法)。
二、Method
Scala的方法与Java的方法类似,都是添加至类中的行为,但是在具体的实现细节上差异很大,下面展示一个参数为整形,返回值为String的方法定义
// java public String doSomething(int x) { // code here } // scala def doSomething(x: Int): String = { // code here }
Scala中的方法可以写的更为简洁,如下
def plusOne(i: Int) = i + 1
将参数值加1后返回,不用显示定义返回值类型,除了已经给出的差别,还有其他差别如下
· 指定方法访问控制(可见性)
· 为方法参数设置默认值的能力
· 调用方法时指定方法参数名称的能力
· 声明方法抛出的异常
· 在方法中使用var类型的字段
2.1 控制方法的范围
1. 问题描述
Scala的方法默认为public的,你需要像Java一样控制方法的范围
2. 解决方案
Scala提供了如下范围控制选项
· 对象私有范围的
· 私有的
· 受保护的
· 包级别的
· 指定包级别的
· 公有的
最严格的访问就是将方法定义为对象私有的,其只能在当前对象上被调用,其他同类型对象无法访问,在方法前面添加private[this]即可
private[this] def isFoo = true
在如下示例中,doFoo的参数为Foo实例,但是isFoo被定义为对象私有方法,如下代码编译不成功
这是因为other无法访问isFoo方法,该方法被定义为只有this(当前对象)可以访问
可以将方法定义为private,这样当前对象可以访问,并且同类型的其他对象也可以访问
当定义为private时,子类无法访问,如下示例编译不会通过
将heartBeat定义为protected之后则可通过编译
Scala中的protected含义与Java不太相同,其在同一个包的其他类无法访问该protected方法,如下示例
package world { class Animal { protected def breathe {} } class Jungle { val a = new Animal a.breathe // error: this line won't compile } }
编译时报错如下
表示Jungle类无法访问同包下的Animal类的方法
为了是方法对于同包下的其他类可见,可以使用private[packageName]来修饰方法
package model { class Foo { private[model] def doX {} private def doY {} } class Bar { val f = new Foo f.doX // compiles f.doY // won't compile } }
运行结果如下
除了让方法在同包下可见,Scala还可以让包在类继承结构中的不同层级下可见
package com.hust.grid.model { class Foo { private[model] def doX {} private[grid] def doY {} private[hust] def doZ {} } } import com.hust.grid.model._ package com.hust.grid.view { class Bar { val f = new Foo f.doX // won't compile f.doY f.doZ } } package com.hust.common { class Bar { val f = new Foo f.doX // won't compile f.doY // won't compile f.doZ } }
其中doX在model包下其他类中可见,doY在grid包下其他类中可见,doZ在hust包下其他类中可见
如果方法没有修饰符,其就是public的,下面示例中任何类都可以调用doX方法
package com.hust.grid.model { class Foo { def doX {} } } package org.xyz.bar { class Bar { val f = new com.hust.grid.model.Foo f.doX } }
3. 讨论
Scala中的访问控制符比Java的要稍微复杂点,需要不断体会和使用
2.2 调用超类的方法
1. 问题描述
为了保持代码的简洁性,你需要调用父类定义的方法
2. 解决方案
在基础使用上,Scala与Java一样,使用super指代超类,然后使用方法名指代具体方法
class WelcomeActivity extends Activity { override def onCreate(bundle: Bundle) { super.onCreate(bundle) // more code here ... } }
如果类继承了多个traits,并且这些traits实现了相同的方法,这时你不仅可以选择方法,并且还可以选择哪个traits
trait Human { def hello = "the Human trait" } trait Mother extends Human { override def hello = "Mother" } trait Father extends Human { override def hello = "Father" }
你可以使用如下不同的方法调用hello方法
class Child extends Human with Mother with Father { def printSuper = super.hello def printMother = super[Mother].hello def printFather = super[Father].hello def printHuman = super[Human].hello }
可以进行如下测试
object Test extends App { val c = new Child println(s"c.printSuper = ${c.printSuper}") println(s"c.printMother = ${c.printMother}") println(s"c.printFather = ${c.printFather}") println(s"c.printHuman = ${c.printHuman}") }
运行结果如下
c.printSuper = Father c.printMother = Mother c.printFather = Father c.printHuman = the Human trait
当类继承多traits并且有多个相同方法时,可以使用super[traitName].methodName 来调用不同父类中的相同方法
值得注意的是,其无法使用到父类的继承结构(如父类的父类),其只能调用其直接继承的父类的方法,如下例所示,super[Animal].walk编译报错
trait Animal { def walk { println("Animal is walking") } } class FourLeggedAnimal extends Animal { override def walk { println("I'm walking on all fours") } } class Dog extends FourLeggedAnimal { def walkThenRun { super.walk // works super[FourLeggedAnimal].walk // works super[Animal].walk // error: won't compile } }
由于Dog并为直接继承Animal,因此不能使用super[Animal].walk来调用Animal的方法
2.3 设置方法参数的默认值
1. 问题描述
你想要定义方法参数值的默认值,以便在调用方法时不用显示定义该方法参数
2. 解决方案
在方法签名中指定默认值,如下所示
class Connection { def makeConnection(timeout: Int = 5000, protocol: = "http") { println("timeout = %d, protocol = %s".format(timeout, protocol)) // more code here } }
timeout的默认值为5000,protocal的默认值为"http",可以通过如下方法调用该方法
c.makeConnection() c.makeConnection(2000) c.makeConnection(3000, "https")
如果你喜欢指定参数名时,可以使用如下方式
c.makeConnection(timeout=10000) c.makeConnection(protocol="https") c.makeConnection(timeout=10000, protocol="https")
3. 讨论
如同构造函数参数一样,你可以为方法参数设置默认值,方法中参数的赋值从左到右,使用如下方法可以使用timeout和protocal的默认值
c.makeConnection()
可以设置timeout值为2000,而protocal使用默认值
c.makeConnection(2000)
可以设置同时设置timeout和protocal的值
c.makeConnection(2000, "https")
但是通过上述方法无法单独设置protocal的值,可以这样单独设置protocal的值,而timeout默认为5000
c.makeConnection(protocol="https")
当你的方法中既有默认值也有非默认值,你需要将由默认值的参数放在最后
可以上述调用都会报错,重新调整参数位置即可