01-scala基础

79 阅读3分钟

类型层次结构

整体层次结构如下: Any是所有类型的基类。值类型和引用类型分别由不同的基类。

值类型可以进行强制转换,转换的方式如下图:

Nothing是所有类型的子类型,没有任何类型可以是Nothing,一般我们用它给出非正常终止信号。Null是所有引用类型的子类型,一般在scala中使用,更多的是在和Java等语言交互时使用。

变量和代码块

scala变量分为可变&不可变类型,分别用varval表示,只有var的才可以更改对应的值

package example

object MyExample {
  def main(args: Array[String]): Unit = {
    val x = 1 + 1
    var y: Int = 1 + 2
    y = x + 1  // y可以在这里改变值
    println(x)
    println(y)
  }
}

scala使用{}一个代码块,一个代码块最后的语句是返回值,也是代码块的类型,举个例子:

package example

object MyExample {
  def main(args: Array[String]): Unit = {
    val n = {
      val x = 10
      val y = 20
      x + y
    }
    println(n.getClass())  // int
    println(n)
  }
}

输出值:

int
30

元组

任意类型的一个组合,使用()表示,Unit本质上是一个空元组()。看几个例子:

package example

object MyExample {
  
  def main(args: Array[String]): Unit = {
    val tp = (10, "foo", 1.1)
    println(tp.toString())
    println(tp._2)
    println(tp.getClass())
  }
}

输出:

(10,foo,1.1)
foo
class scala.Tuple3

函数和方法

scala中,函数是一个带有参数的表达式,举个列子:

package example

object MyExample {
  def main(args: Array[String]): Unit = {
    val add = (x: Int, y: Int) => x + y
    val x = 10
    val y = 20
    val z = add(x, y)
    println(z)
  }
}

上面的add就是一个表达式,也就是函数。当然,表达式也可以使用{}确定范围,举个例子:

package example

object MyExample {
  def main(args: Array[String]): Unit = {
    val add = (x: Int, y: Int) => {  // 这里使用{}表示范围
      println(s"In function, x: $x, y: $y")
      x + y
    }
    val x = 10
    val y = 20
    val z = add(x, y)
    println(z)
  }
}

{}最后一行即返回类型。println(s"In function, x: $x, y: $y")中,s表示使用字符串插值,内部利用$就是实际变量的值,类似shell

方法和函数有差异,需要使用def定义名称,而且必须有返回值。举个例子:

package example

object MyExample {
  
  // 定义方法
  def Add(x: Int, y: Int): Int = {
    x + y
  }

  def main(args: Array[String]): Unit = {
    val x = 10
    val y = 20
    val z = Add(x, y)
    println(z)
  }
}

上面的Add就是一个方法,注意是=后面跟着{}。如果方法只有一行,也可以简写为以下的形式:

def Add(x: Int, y: Int): Int = x + y
def Add(x: Int, y: Int) = x + y  // 自动推导返回值

方法也可以使用默认参数,也可以制定参数名称,举个例子:

package example
object MyExample {

  def greet(firstName: String, lastName: String = "Foo", age: Int = 10): Unit = {
    println(s"hello [$firstName $lastName], you are $age years old now")
  }

  def main(args: Array[String]): Unit = {
    greet("Tom")
    // 使用参数名称赋值
    greet(lastName = "Dim", firstName = "Tom")
  }
}

代码输出:

hello [Tom Foo], you are 10 years old now
hello [Tom Dim], you are 10 years old now

默认参数规则和C++默认参数一样。

同样的,我们可以使用多个参数列表,给出代码实例:

package example
object MyExample {

  def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier

  def main(args: Array[String]): Unit = {
    val x = 2
    val y = 4
    val z = addThenMultiply(x, y)(3)
    println(z)
  }
}

类和对象

使用class可以声明类,并可以定义一些方法,我们可以使用new创建对象

package example

class Person {
  var age: Int = 0
  var name: String = ""

  def getInfo(): String = {
    println("int class method Person.getInfo")
    s"name: $name, age: $age"
  }
}
object MyExample {

  def main(args: Array[String]): Unit = {
    val p = new Person()  // 注意这里是val
    p.age = 10
    p.name = "foo"
    val info = p.getInfo()
    println(info)
  }
}

输出:

int class method Person.getInfo
name: foo, age: 10

注意一个地方,pval只是说明p不能指向其他对象,而不是p本身指向的class的成员不能变

Java等有static成员的概念,scala中直接在伴生对象中定义静态成员,举个例子:

package example

class Person {
  var age: Int = 0
  var name: String = ""

  def getInfo(): String = {
    println("int class method Person.getInfo")
    s"name: $name, age: $age"
  }
}

object Person {
  def create(name: String, age: Int): Person = {
    val p = new Person()
    p.name = name
    p.age = age
    p
  }
  
  var counter = 0
}
object MyExample {

  def main(args: Array[String]): Unit = {
    // 直接使用方法名即可
    val p1 = Person.create("foo1", 11)
    val p2 = Person.create("foo2", 12)

    println(p1.getInfo())
    println(p2.getInfo())
    println(s"person counter: ${Person.counter}")
  }
}

代码输出:

int class method Person.getInfo
name: foo1, age: 11
int class method Person.getInfo
name: foo2, age: 12
person counter: 2

类也可以使用构造器,给出代码实例:

package example

class Person(val name: String, age: Int = 10) {
  def greet(): Unit = println(s"Hello $name, you are $age years old")
}

object MyExample {
  
  def main(args: Array[String]): Unit = {
    val p = new Person("foo")
    p.greet()
  }
}

构造参数也可以使用默认参数,构造器中的参数就相当于内部的public成员变量

还有一种是case class,该类型有点类似于struct这种的,内部一般是不可变对象,可以直接比较,举个例子:

package example

case class Person(name: String, age: Int)
object MyExample {

  def main(args: Array[String]): Unit = {
    val p1 = Person("foo", 10)
    val p2 = Person("foo2", 11)
    val p3 = Person("foo", 10)
    println(p1 == p2)
    println(p1 == p3)
  }
}

代码输出:

false
true

Trait

trait类似Java的interface,可以定义一组trait并重载方法,trait之间也可以重载,举个例子:

package example

trait AnimalAction {
  val name: String  // 必须在构造器中构造
  def run(): Unit = println("run")
  def eat(): Unit
}

// 直接
class Bird(val name: String = "foo") extends AnimalAction {

  override def run(): Unit = {
    println(s"$name is running")
  }

  override def eat(): Unit = {
    println(s"$name is eatting")
  }
}


object MyExample {
  
  def animalDo(action: AnimalAction): Unit = {
    action.run()
    action.eat()
  }

  def main(args: Array[String]): Unit = {
    val b = new Bird("noky")
    animalDo(b)
  }
}

代码输出:

noky is running
noky is eatting

通过mixin来组合类

mixin:某个trait被用来组合类。举个代码实例:

package example

abstract class A {
  val message: String
}

class B extends A {
  val message = "i am a message"
}

trait C extends A {
  def loadMsg = message.toUpperCase()
}

class D extends B with C {}

object MyExample {
  
  def main(args: Array[String]): Unit = {
    val d = new D()
    println(d.loadMsg)
  }
}

代码输出

I AM A MESSAGE

这里面的C相当于mixin。借助Trait实现新的操作,这里重点在于有新的方法,这就解决了单一继承的痛点

scala中,一个类只能有一个父类,但是可以有多个mixin