Class Object: 方法

控制方法作用域

Scala 方法默认为 public,可见性的控制方法与 Java 类似,但是提供比 Java 更细粒度更有力的控制方式:

  • 对象私有(object-private)
  • 私有(private)
  • 包(package)
  • 指定包(package-specific)
  • 公共(public)

对象私有域

只有该对象的当前实例能够访问该方法,相同类的其他实例无法访问:

private[this] def isFoo = true

私有域

该类或该类的所有实例都能访问该方法:

private def isFoo = true

保护域

只有子类能够访问该方法:

protected def breathe {}

在 Java 中,该域方法可以被当前包(package)的其他类访问,但是在 Scala 中不可以。

包域

当前包中所有成员都可以访问:

package com.acme.coolapp.model {
  class Foo { 
    private[model] def doX {} 	// 定义为包域,model 包中所有成员可以访问
    private def doY {}
  }
}

更多级别的包控制

package com.acme.coolapp.model { 
  class Foo { 
    private[model] def doX {}		// 指定到 model 级别
    private[coolapp] def doY {} 	// 指定到 coolapp 级别
    private[acme] def doZ {}		// 指定到 acme 级别
  }
}

公共域

如果没有任何作用域的声明,即为公共域。

调用父类方法

可以在子类中调用父类或特质中已存在的方法来复用代码:

class WelcomeActivity extends Activity { 
  override def onCreate(bundle: Bundle) { 
    super.onCreate(bundle) 
    // more code here ... 
  } 
}

指定调用不同特质中的方法

如果同时继承了多个特质,并且这些特质都实现了相同的方法,这时不但能指定调用方法名,还能指定调用的特质名:

trait Human {
  def hello = "the Human trait" 
}

trait Mother extends Human {
  override def hello = "Mother" 
}

trait Father extends Human {
  override def hello = "Father" 
}

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 
}

但是并不能跨级别的调用,比如:

trait Animal
class Pets extends Animal
class Dog extends Pets

这时 Dog 只能指定 Pets 中的方法,不能再指定 Animal 中的方法,除非显示继承了 Animal。

指定默认参数值

def makeConnection(timeout: Int = 5000, protocol: = "http") { 
  println("timeout = %d, protocol = %s".format(timeout, protocol)) 
  // more code here 
}
c.makeConnection() 			// 括号不能省略,除非方法定义中没有参数
c.makeConnection(2000) 
c.makeConnection(3000, "https")

如果方法有一个参数为默认,而其他参数并没有提供默认值:

def makeConnection(timeout: Int = 5000, protocol: String)
// error: not enough arguments for method makeConnection:
c.makeConnection("https") 

这时任何只提供一个参数值的调用都会报错,可以将定义中带有默认值的参数放在后面,然后就可以通过一个参数来调用:

def makeConnection(protocol: String, timeout: Int = 5000)
makeConnection("https")

调用时提供参数名

methodName(param1=value1, param2=value2, ...)

通过参数名提供参数时,参数顺序没有影响。

方法返回值为元组

def getStockInfo = { 
  // other code here ... 
  ("NFLX", 100.00, 10) // this is a Tuple3 
}

val (symbol, currentPrice, bidPrice) = getStockInfo
val (symbol:String, currentPrice:Double, bidPrice:Int) = getStockInfo

无括号的访问器方法调用

class Pizza { 
  // no parentheses after crustSize 
  def crustSize = 12 
}
val p = new Pizza
p.crustSize

推荐的策略是在调用没有副作用的方法时使用无括号的方式调用。

在纯的函数式编程中不存在副作用,副作用包括:

  • 写入或打印输出
  • 读取输入
  • 修改作为输入的变量的状态
  • 抛出异常,或错误发生时终止程序
  • 调用其他有副作用的函数

接收多变量参数

def printAll(strings: String*) {
  strings.foreach(println) 
}
printAll("a","b","c")
val list = List(1,2,3)
printAll(list:_*)

如果方法拥有多个参数,其中一个是多变量,则这个参数要放在参数列表的末端:

def printAll(i: Int, strings: String*)

声明一个能够抛出异常的方法

如果想要声明一个方法,该方法可能会抛出异常:

@throws(classOf[Exception]) 
override def play {
  // exception throwing code here ... 
}

@throws(classOf[IOException]) 
@throws(classOf[LineUnavailableException]) @throws(classOf[UnsupportedAudioFileException])
def playSoundFileWithJavaAudio {
  // exception throwing code here ... 
}

作用是用于提醒调用者或者与 Java 集成。

支持流式风格编程

如果想要支持调用者以流式方式调用,即方法链接,如下面的方式:

person.setFirstName("Leonard").setLastName("Nimoy")
		.setAge(82) 
		.setCity("Los Angeles") 
		.setState("California")

为了支持这种方式,需要遵循以下原则:

  • 如果你的类会被继承,指定this.type作为方法返回值类型
  • 如果确定你的类不会被继承,你可以直接在方法中返回this
class Person { 
  protected var fname = "" 
  protected var lname = "" 
  def setFirstName(firstName: String): this.type = { 
    fname = firstName 
    this 
  }
  def setLastName(lastName: String): this.type = {
    lname = lastName 
    this 
  }
}

class Employee extends Person { 
  protected var role = "" 
  def setRole(role: String): this.type = { 
    this.role = role
    this 
  } 
  override def toString = {
	"%s, %s, %s".format(fname, lname, role) 
  }
}

然后我们就可以以流式的风格调用方法:

object Main extends App { 
  val employee = new Employee 
  // use the fluent methods 
  employee.setFirstName("Al") 
  			.setLastName("Alexander") 
  			.setRole("Developer") 
  println(employee)
}

如上面的原则所述,如果确定这个类不会被继承,并不需要在 set* 类型的方法中指定this.type作为返回值类型,这种情况可以省略,只需要在方法中返回 this 的引用即可。