操作符重载
scala中的所有操作符都是方法. 1 + 2 事实上是 1.+(2),即调用1的”+”方法并传入参数2.并且调用无参方法时可以省略方法名前的”.”操作符,比如 2 toString.
Characters:Scala中允许所有的ASCII字符,包括\u0020到\u007F之间的所有字符.
保留关键字不可以重用:所有保留的关键字均不可重用,字符”_”也是一个保留的关键字.
清晰的标示符:一个清晰的标示符可以以字母或下划线开始,后跟字母,数字或下划线.
无参方法
定义方法时,如果该方法没有参数列表,则可以省略方法名后括号,如果不省略该括号,则调用时即可以带括号也可以不带括号,通常的做法是无参方法均不添加括号,调用时也不添加括号,这样比较统一.
优先规则
scala中的运算优先级:
- All letters
- |
- ^
- &
- < >
- = !
- :
- -
- / %
- All other special characters
特定领域语言
特定领域语言,或者说DSLs,是指针对特殊问题领域的设计语言,其目标是以更加简明直观的方式表达该领域中的概念.比如SQL就可以视为DSL,因为它是编程语言来表达的关系模型的解释.
scala同时支持内嵌式的DSL和需要解析器支持的外部DSL,其灵活的语法规则对中缀和后缀的方法调用语法提供了出色的支持,可以很好的使用scala语法结构编写嵌入式DSL.
这是一个BDD的实例,展示了非常自然的DSL语法:
import org.scalatest.{ FunSpec, ShouldMatchers }
class NerdFinderSpec extends FunSpec with ShouldMatchers {
describe ("nerd finder") {
it ("identify nerds from a List") {
val actors = List("Rick Moranis", "James Dean", "Woody Allen")
val finder = new NerdFinder(actors)
finder.findNerds shouldEqual List("Rick Moranis", "Woody Allen")
}
}
}
IF语句
scala中的if语句与java类似提供同样的功能:
if(2+2==5){
println("Hello from 1984.")
}elseif(2+2==3){
println("Hello from Remedial Math class?")
}else{
println("Hello from a non-Orwellian future.")
}
但最大的不同是scala中的语句返回值,因此可以将一段if表达式当做一个值赋给变量:
val configFile = new java.io.File("somefile.txt")
val configFilePath = if (configFile.exists()) {
configFile.getAbsolutePath()
}else{
configFile.createNewFile()
configFile.getAbsolutePath()
}
满足条件的分支计算后所得的结果将会赋给变量configFilePath,而值的类型会由编译器自动推断为所有分支类型的最接近父类.
FOR表达式
for循环:
val dogBreeds = List("Doberman", "Yorkshire Terrier", "Dachshund",
"Scottish Terrier", "Great Dane", "Portuguese Water Dog")
for (breed <- dogBreeds)
println(breed)
生成器表达式:
“breed <- dogBreeds”实质上是一个生成器表达式,因为它从一个集合中不停的生成值,”<-“操作符遍历集合中的每个值.
for (i <- 1 to 10) println(i)
过滤器:
for (breed <- dogBreeds
if !breed.startsWith("Yorkshire") // if后跟一个boolean守卫,或多个
if breed.contains("Terrier") && !breed.startsWith("Yorkshire")
) println(breed)
Yielding
除了循环打印集合中的值,或许需要使用另一个集合来处理当前集合中的部分值,使用yield关键字则可以在for表达式中生成新的集合:
val dogBreeds = List("Doberman", "Yorkshire Terrier", "Dachshund",
"Scottish Terrier", "Great Dane", "Portuguese Water Dog")
val filteredBreeds = for {
breed <- dogBreeds
if breed.contains("Terrier") && !breed.startsWith("Yorkshire")
} yield breed // 满足if条件的元素会生成一个新的集合赋值给filteredBreeds变量,集合类型与原始集合相同
与循环不同的是,循环时for后面跟括号,而yield时使用分号.
while循环
import java.util.Calendar
def isFridayThirteen(cal: Calendar): Boolean = {
val dayOfWeek = cal.get(Calendar.DAY_OF_WEEK)
val dayOfMonth = cal.get(Calendar.DAY_OF_MONTH)
// Scala returns the result of the last expression in a method
(dayOfWeek == Calendar.FRIDAY) && (dayOfMonth == 13)
}
while (!isFridayThirteen(Calendar.getInstance())) {
println("Today isn't Friday the 13th. Lame.")
// sleep for a day
Thread.sleep(86400000)
}
do-while循环
var count = 0
do{
count += 1
println(count)
} while (count < 10)
条件操作符
- “&&”
- “||”
- “>”
- “>=”
- “<”
- “<=”
- “==”
- “!=”
“&&”和”||”是短路操作符,一旦达到条件就会停止运算.
异常处理
scala鼓励通过使用功能构造和强类型来减少异常处理的出现,但仍然不能避免,特别是与java代码交接的地方.与java不同,scala中没有受检异常.
object TryCatch {
/** Usage: scala rounding.TryCatch filename1 filename2 ... */
def main(args: Array[String]) = {
args foreach (arg => countLines(arg))
}
import scala.io.Source //
import scala.util.control.NonFatal
def countLines(fileName: String) = {
println()
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
}
}
}
}
lazy val
懒值,推迟对值的加载,只有在第一次调用时才会进行计算,必须是不可变类型,常用场景:
- 表达式消耗比较高,比如打开一次数据库连接.
- 推迟一些并不是立即需要的工作的启动时间.
有时对象中的字段需要计算才能得到,只有在需要的时候再去计算.
class Book(name:String){
println("new book"+name) override def toString() = "《"+name+"》"
}
lazy val b = new Book(“Java”)
println(“Test”)
println(b.toString)res:
Test // 首先打印Test而没有初始化b
new bookJava
《Java》
枚举
scala中的枚举实现与java完全不同:
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")
}
import Breed._
// Usage
// print a list of breeds and their IDs
println("ID\tBreed")
for (breed <- Breed.values) println(s"${breed.id}\t$breed")
// print a list of Terrier breeds
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
另一个实例:
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
import WeekDay._
def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
WeekDay.values filter isWorkingDay foreach println
格式化字符串
val name = "Buck Trends"
println(s"Hello, $name")
valgross =100000F
val net = 64000F
val percent = (net / gross) * 100
println(f"$$${gross}%.2f vs. $$${net}%.2f or ${percent}%.1f%%")
res0: $100000.00 vs. $64000.00 or 64.0%
scala> f"${i}%.2f"
res4: String = 200.00
scala> val s = "%02d: name = %s".format(5, "Dean Wampler")
s: String = "05: name = Dean Wampler"
特质:接口和混入
java中的接口允许声明方法但不允许定义方法,scala中使用trait代替了java中的接口.
我们创建一个服务端然后混入日志类:
class ServiceImportante(name: String) {
def work(i: Int): Int = {
println(s"ServiceImportante: Doing important work! $i")
i+1
}
}
val service1 = new ServiceImportante("uno")
(1 to 3) foreach (i => println(s"Result: ${service1.work(i)}"))
输出:
ServiceImportante: Doing important work! 1
Result: 2
ServiceImportante: Doing important work! 2
Result: 3
ServiceImportante: Doing important work! 3
Result: 4
然后如何混入一个日志类呢,这里会简单的使用println方法.下面有两个特质,一个是抽象特质,没有具体的成员,另一个特质实现了当一个抽象特质中的方法用于输出日志:
trait Logging {
definfo (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")
}
Logging的代码与java中的interface实现基本一致.最后声明一个server实例并混入日志类:
val service2 = new ServiceImportante("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
}
}
(1 to 3) foreach (i => println(s"Result: ${service2.work(i)}"))
使用”with”关键字混入StdoutLogging特质,然后就可以使用StdoutLogging特质中定义的所有方法.当然也可以在定义server类时直接混入,而不是在创建server实例时再混入.