类型层次结构
整体层次结构如下:
Any
是所有类型的基类。值类型和引用类型分别由不同的基类。
值类型可以进行强制转换,转换的方式如下图:
Nothing
是所有类型的子类型,没有任何类型可以是Nothing
,一般我们用它给出非正常终止信号。Null
是所有引用类型的子类型,一般在scala
中使用,更多的是在和Java
等语言交互时使用。
变量和代码块
scala变量分为可变&不可变类型,分别用var
个val
表示,只有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
注意一个地方,p
是val
只是说明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