定义无参数方法可以省略括号。一旦定义无参数方法时省略了括号,那么调用这些方法时必须省略括号。假如在定义无参数方法时添加了空括号,那么调用方可以选择省略或保留括号。
Scala社区已经养成了这样一个习惯:定义那些无副作用的无参方法时省略括号。而定义具有副作用的方法时则添加括号,这样便能提醒读者某些对象可能会发送变化,需要额外小心。
加入运行scala或scalac时添加了-Xlint
选项,那么在定义那些会产生副作用(例如方法中有IO操作)的无参方法时,省略括号将会出现一条警告信息。
// 合理考虑是否使用括号有助于构建更具有表现力的方法调用链
def isEven(n: Int) = (n % 2) == 0
List(1, 2, 3, 4).filter((i: Int) => isEven(i)).foreach((i: Int) => print(i))
println
List(1, 2, 3, 4).filter(i => isEven(i)).foreach(i => print(i))
println
List(1, 2, 3, 4).filter(isEven).foreach(print)
println
List(1, 2, 3, 4) filter isEven foreach print
24 24 24 24
defined function isEven
推导式起源于函数式编程,它表示:遍历一个或多个集合,对集合中的元素进行“推导”,并从中计算出新的事物,新推导出的事物往往是另一个集合。
val dogBreeds = List("Doberman", "Yorkshire Terrier", "Dachshund",
"Scottish Terrier", "Great Dane", "Portuguese Water Dog")
// 使用if守护式(guard)
for(breed <- dogBreeds
if breed.contains("Terrier"))
println(breed)
Yorkshire Terrier Scottish Terrier
dogBreeds: List[String] = List( "Doberman", "Yorkshire Terrier", "Dachshund", "Scottish Terrier", "Great Dane", "Portuguese Water Dog" )
for(breed <- dogBreeds
if breed.contains("Terrier")
if !breed.startsWith("Yorkshire"))
println(breed)
Scottish Terrier
// 使用yield关键字能在for表达式中生成新的集合
val filteredBreeds = for {
breed <- dogBreeds
if breed.contains("Terrier") && !breed.startsWith("Yorkshire")
} yield breed
println(filteredBreeds)
List(Scottish Terrier)
filteredBreeds: List[String] = List("Scottish Terrier")
val dogBreeds2 = List(Some("Doberman"), None, Some("Yorkshire Terrier"), Some("Dachshund"), None,
Some("Scottish Terrier"), Some("Great Dane"), None, Some("Portuguese Water Dog"))
dogBreeds2: List[Option[String]] = List( Some(Doberman), None, Some(Yorkshire Terrier), Some(Dachshund), None, Some(Scottish Terrier), Some(Great Dane), None, Some(Portuguese Water Dog) )
for {
Some(breed) <- dogBreeds2
upcasedBreed = breed.toUpperCase()
} println(upcasedBreed)
DOBERMAN YORKSHIRE TERRIER DACHSHUND SCOTTISH TERRIER GREAT DANE PORTUGUESE WATER DOG
上式中,在for表达式中定义upcasedBreed,然后在后面的表达式中使用该值。
由于dogBreeds2列表中包含都是Option对象,在for推导式中使用模式匹配提取出Some类型,而不处理None元素。
在大部分语言中,可以使用break或continue跳出循环。而Scala并未提供该功能。编写地道的Scala代码,可以使用条件表达式或者使用递归判断循环是否应该继续。在一开始对集合进行过滤以消除循环中的复杂条件就更好了,不过为了满足break的需求,Scala提供scala.util.control.Breaks
对象来实现break功能。
// break/continue的例子
import util.control.Breaks._
import util.control.Breaks._
println("==== BREAK EXAMPLE ====")
breakable {
for (i <- 1 to 10) {
println(i)
if (i > 4) break // break out of the for loop
}
}
println("==== CONTINUE EXAMPLE ====")
val searchMe = "peter piper picked a peck of pickled peppers"
var numPs = 0
for (i <- 0 until searchMe.length) {
breakable {
if (searchMe.charAt(i) != 'p') {
break // break out of the 'breakable', continue the outside loop
} else {
numPs += 1
}
}
}
println("Found " + numPs + " p's in the string.")
==== BREAK EXAMPLE ==== 1 2 3 4 5 ==== CONTINUE EXAMPLE ==== Found 9 p's in the string.
searchMe: String = "peter piper picked a peck of pickled peppers" numPs: Int = 9
// 资源管理场景中处理异常
// 统计文件行数
import scala.io.Source
import scala.util.control.NonFatal
def countLines(fileName: String) = {
var source: Option[Source] = None
try {
source = Some(Source.fromFile(fileName))
val size = source.get.getLines.size
println(s"file $fileName has $size lines")
} catch {
case NonFatal(ex) => println(s"Non fatal exception! $ex")
} finally {
for (s <- source) {
println(s"Closing $fileName ...")
s.close
}
}
}
import scala.io.Source import scala.util.control.NonFatal defined function countLines
val files = Array("fileBulkReaderTestFile.txt", "nonexistentFile.txt")
files foreach (fileName => countLines(fileName))
file fileBulkReaderTestFile.txt has 3 lines Closing fileBulkReaderTestFile.txt ... Non fatal exception! java.io.FileNotFoundException: nonexistentFile.txt (系统找不到指定的文件。)
files: Array[String] = Array("fileBulkReaderTestFile.txt", "nonexistentFile.txt")
当希望以延迟的方式初始化某值,并且表达式不会被重复计算,则需要使用惰性求值。
常见场景:
object ExpensiveResource {
lazy val resource: Int = init()
def init(): Int = {
Thread.sleep(3000)
0
}
}
defined object ExpensiveResource
ExpensiveResource.resource
res14: Int = 0
惰性求值与方法调用的区别:
Scala在标准库中专门定义Enumeration类来实现枚举,并未提供任何特殊语法支持枚举。
值得一提的是,Scala常常使用case class来代替枚举值,其有两点好处:
object Breed extends Enumeration {
type Breed = Value
val doberman = Value("Doberman Pinscher")
val yorkie = Value("Yorkshire Terrier")
val scottie = Value("Scottish Terrier")
val dane = Value("Great Dane")
val portie = Value("Portuguese Water Dog")
}
defined object Breed
import Breed._
println("ID\tBreed")
for (breed <- Breed.values) println(s"${breed.id}\t$breed")
println("\nJust Terriers:")
Breed.values filter (_.toString.endsWith("Terrier")) foreach println
def isTerrier(b: Breed) = b.toString.endsWith("Terrier")
println("\nTerriers Again:")
Breed.values filter isTerrier foreach println
ID Breed 0 Doberman Pinscher 1 Yorkshire Terrier 2 Scottish Terrier 3 Great Dane 4 Portuguese Water Dog Just Terriers: Yorkshire Terrier Scottish Terrier Terriers Again: Yorkshire Terrier Scottish Terrier
import Breed._ defined function isTerrier
Scala提供了很多个Value的重载方法:
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
defined object WeekDay
import WeekDay._
def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
WeekDay.values filter isWorkingDay foreach println
Mon Tue Wed Thu Fri
import WeekDay._ defined function isWorkingDay
// 在应用中混入日志
class ServiceImportance(name: String) {
def work(i: Int): Int = {
println(s"ServiceImportance: Doing important work! $i")
i + 1
}
}
val service1 = new ServiceImportance("uno")
(1 to 3) foreach (i => println(s"Result: ${service1.work(i)}"))
ServiceImportance: Doing important work! 1 Result: 2 ServiceImportance: Doing important work! 2 Result: 3 ServiceImportance: Doing important work! 3 Result: 4
defined class ServiceImportance service1: $user.ServiceImportance = cmd19$$user$ServiceImportance@19e3ffe
// 在服务中混入标准日志库,使用println输出日志
// 定义日志抽象
trait Logging {
def info(message: String): Unit
def warning(message: String): Unit
def error(message: String): Unit
}
// 定义日志信息输出到标准输出
trait StdoutLogging extends Logging {
def info(message: String) = println(s"INFO: $message")
def warning(message: String) = println(s"WARNING: $message")
def error(message: String) = println(s"ERROR: $message")
}
defined trait Logging defined trait StdoutLogging
val service2 = new ServiceImportance("dos") with StdoutLogging {
override def work(i: Int): Int = {
info(s"Starting work: i = $i")
val result = super.work(i)
info(s"Ending work: i = $i, result = $result")
result
}
}
service2: ServiceImportance with StdoutLogging = cmd21$$user$$anonfun$1$$anon$1@110bafd
(1 to 3) foreach (i => println(s"Result: ${service2.work(i)}"))
INFO: Starting work: i = 1 ServiceImportance: Doing important work! 1 INFO: Ending work: i = 1, result = 2 Result: 2 INFO: Starting work: i = 2 ServiceImportance: Doing important work! 2 INFO: Ending work: i = 2, result = 3 Result: 3 INFO: Starting work: i = 3 ServiceImportance: Doing important work! 3 INFO: Ending work: i = 3, result = 4 Result: 4