[关闭]
@zwenqiang 2015-12-09T18:14:23.000000Z 字数 4916 阅读 2880

Scala基础语法

Scala


表达式和值

启动解释器

  1. $ scala
  2. Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
  3. Type in expressions to have them evaluated.
  4. Type :help for more information.
  5. scala>

表达式

  1. scala> 1 + 1
  2. res0: Int = 2

值(常量)

你可以给一个表达式的结果起个名字赋成一个不变量(val)

  1. scala> val two = 1 + 1
  2. two: Int = 2
  3. scala> two = 2
  4. <console>:8: error: reassignment to val
  5. two = 2
  6. ^

变量

如果你需要修改这个名称和结果的绑定,可以选择使用var

  1. scala> var name = "steve"
  2. name: java.lang.String = steve
  3. scala> name = "marius"
  4. name: java.lang.String = marius

函数(function)

你可以使用def创建函数.

  1. scala> def addOne(m: Int): Int = m + 1
  2. addOne: (m: Int)Int
  3. # 其中m为参数,冒号后为参数类型,括号为Int为返回值类型。可以不写,编译器自动识别。

如果函数不带参数,你可以不写括号

  1. scala> def three() = 1 + 2
  2. three: ()Int
  3. scala> three()
  4. res2: Int = 3
  5. scala> three
  6. res3: Int = 3

匿名函数

  1. scala> (x: Int) => x + 1
  2. res2: (Int) => Int = <function1>

这个函数为参数为xInt变量加1。

  1. scala> res2(1)
  2. res3: Int = 2

你可以传递匿名函数,或将其保存成不变量。

  1. scala> val addOne = (x: Int) => x + 1
  2. addOne: (Int) => Int = <function1>
  3. scala> addOne(1)
  4. res4: Int = 2

如果你的函数有很多表达式,可以使用{}来格式化代码,使之易读

  1. def timesTwo(i: Int): Int = {
  2. println("hello world")
  3. i * 2 //返回值为Int
  4. }

对匿名函数也是这样的。

  1. scala> { i: Int =>
  2. println("hello world")
  3. i * 2
  4. }
  5. res0: (Int) => Int = <function1>
  6. 等价于 (i: Int) =>{
  7. println("hello world")
  8. i * 2
  9. }

函数是一等公民

  1. //函数值
  2. val squareVal = (a: Int) => a * a
  3. //函数作为参数传递
  4. def addOne(f:Int => Int, arg:Int) = f(arg) + 1
  5. println("addOne(squareVal,2):" + addOne(squareVal, 2)) // 结果为6

按名称传递参数

  1. val logEnable = false
  2. val MSG = "programing is running"
  3. def log(msg: String) =
  4. if (logEnable) println(msg)
  5. else println("programing is exit")
  6. log(MSG + 1 / 0)

因为程序中出现了1 / 0,所以程序运行的话会出现异常, 但是修改为按名称传递后将不会产生异常。

  1. def log(msg: => String) =
  2. if (logEnable) println(msg)
  3. else println("programing is exit")

因为log函数的参数是按名称传递,参数会等到实际使用的时候才会计算,所以被跳过。
按名称传递参数可以减少不必要的计算和异常。

部分应用(Partial application)

你可以使用下划线“_”部分应用一个函数,结果将得到另一个函数。Scala使用下划线表示不同上下文中的不同事物,你通常可以把它看作是一个没有命名的神奇通配符。在{ _ + 2 }的上下文中,它代表一个匿名参数。你可以这样使用它:

  1. scala> def adder(m: Int, n: Int, i: Int) = m + n + i
  2. adder: (m: Int, n: Int, i: Int)Int
  3. scala> val add2 = adder(5, _: Int, 10)
  4. add2: Int => Int = <function1>
  5. scala> add2(7)
  6. res25: Int = 22

鸭子类型

  1. { def close(): Unit }

作为参数类型。因此任何含有close()的函数的类都可以作为参数。
不必使用继承这种不够灵活的特性。

  1. def withClose(closeAble: { def close(): Unit }, op: { def close(): Unit } => Unit) {
  2. try {
  3. op(closeAble)
  4. } finally {
  5. closeAble.close()
  6. }
  7. }
  8. class Connection {
  9. def close() = println("close Connection")
  10. val conn: Connection = new Connection()
  11. withClose(conn, conn => println("do something with Connection"))

柯里化函数

  1. def add(x:Int, y:Int) = x + y

是普通的函数

  1. def add(x:Int) = (y:Int) => x + y

是柯里化后的函数,相当于返回一个匿名函数表达式。

  1. def add(x:Int)(y:Int) = x + y

有时会有这样的需求:允许别人一会在你的函数上应用一些参数,然后又应用另外的一些参数。

  1. scala> def multiply(m: Int)(n: Int): Int = m * n
  2. multiply: (m: Int)(n: Int)Int
  3. // 你可以直接传入两个参数。
  4. scala> multiply(2)(3)
  5. res0: Int = 6
  6. // 你可以填上第一个参数并且部分应用第二个参数。加 _ 作为部分应用函数
  7. scala> val timesTwo = multiply(2) _
  8. timesTwo: (Int) => Int = <function1>
  9. scala> timesTwo(3)
  10. res1: Int = 6

也可以

  1. scala> def multiply(m: Int) = (n: Int) => m * n
  2. multiply: (m: Int)Int => Int
  3. scala> val timesTwo = multiply(2)
  4. timesTwo: Int => Int = <function1>
  5. scala> timesTwo(5)
  6. res36: Int = 10

可变长度参数

Scala在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala中使用“*”来指明该参数为重复参数

  1. def printf(text: String, xs: Any*) = Console.print(text.format(xs: _*))

例如要在多个字符串上执行String的capitalize函数,可以这样写:

  1. def capitalizeAll(args: String*) = {
  2. args.map { arg =>
  3. arg.capitalize
  4. }
  5. }
  6. scala> capitalizeAll("rarity", "applejack")
  7. res2: Seq[String] = ArrayBuffer(Rarity, Applejack)
  1. def printA (args: String *) =
  2. for (arg <- args)
  3. println(arg)
  4. scala> printA ("1", "2", "3")
  5. 1
  6. 2
  7. 3

在函数内部,变长参数的类型,实际为一数组,比如上例的String * 类型实际为 Array[String]。 然而,如今你试图直接传入一个数组类型的参数给这个参数,编译器会报错:

  1. scala> var arr = Array("hello", "world", "goodbye")
  2. arr: Array[String] = Array(hello, world, goodbye)
  3. scala> printA(arr)
  4. <console>:10: error: type mismatch;
  5. found : Array[String]
  6. required: String
  7. printA(arr)
  8. ^

为了避免这种情况,你可以通过在变量后面添加 _*来解决,这个符号告诉Scala编译器在传递参数时逐个传入数组的每个元素,而不是数组整体。

  1. scala> printA(arr: _*)
  2. hello
  3. world
  4. goodbye

  1. class Person(val firstName: String, val lastName: String) {
  2. private var _age = 0
  3. def age = _age
  4. def age_=(newAge: Int) = _age = newAge
  5. def fullName() = firstName + " " + lastName
  6. override def toString() = fullName()
  7. }

构造函数

构造函数不是特殊的方法,他们是除了类的方法定义之外的代码。让我们扩展计算器的例子,增加一个构造函数参数,并用它来初始化内部状态。

  1. class Calculator(brand: String) {
  2. /**
  3. * A constructor.
  4. */
  5. val color: String = if (brand == "TI") {
  6. "blue"
  7. } else if (brand == "HP") {
  8. "black"
  9. } else {
  10. "white"
  11. }
  12. // An instance method.
  13. def add(m: Int, n: Int): Int = m + n
  14. }

你可以使用构造函数来构造一个实例:

  1. scala> val calc = new Calculator("HP")
  2. calc: Calculator = Calculator@1e64cc4d
  3. scala> calc.color
  4. res0: String = black

函数与方法

函数和方法在很大程度上是可以互换的。由于函数和方法是如此的相似,你可能都不知道你调用的东西是一个函数还是一个方法。而当真正碰到的方法和函数之间的差异的时候,你可能会感到困惑。

  1. scala> class C {
  2. | var acc = 0
  3. | def minc = { acc += 1 } //定义方法
  4. | val finc = { () => acc += 1 } // 定义函数
  5. | }
  6. defined class C
  7. scala> val c = new C
  8. c: C = C@1af1bd6
  9. scala> c.minc // calls c.minc()
  10. scala> c.finc // returns the function as a value:
  11. res2: () => Unit = <function0>

继承

  1. class ScientificCalculator(brand: String) extends Calculator(brand) {
  2. def log(m: Double, base: Double) = math.log(m) / math.log(base)
  3. }

重载

  1. class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
  2. def log(m: Int): Double = log(m, math.exp(1))
  3. }

特质(Trait)

特质是一些字段和行为的集合,Traits就像是有函数体的Interface,可以扩展或混入(mixin)你的类中。

  1. trait Car {
  2. val brand: String
  3. }
  4. trait Shiny {
  5. val shineRefraction: Int
  6. }
  7. class BMW extends Car {
  8. val brand = "BMW"
  9. }

通过with关键字,一个类可以扩展多个特质

  1. class BMW extends Car with Shiny {
  2. val brand = "BMW"
  3. val shineRefraction = 12
  4. }

泛型

对函数参数是泛型的,来适用于所有类型。使用[]引入泛型,在使用确定泛型的参数类型。

  1. trait Cache[K, V] {
  2. def get(key: K): V
  3. def put(key: K, value: V)
  4. def delete(key: K)
  5. }

方法也可以引入类型参数。

  1. def remove[K](key: K)

参考

Scala 课堂
Scala 指南

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注