如果类没有内容(body),可以省略空的花括号,只写一行语句
class User
val user1 = new User
类可以带参数,类名称C
之后圆括号内的参数,x
称为类参数class parameter,Scala编译器会使用类参数创建出一个主构造函数paimary constructor
class C(x: R)
私有主构造函数,只在被类本身以及伴生对象访问
class class C(private x: R)
Scala编译器会将编译类内部的任何代码,如果代码不是字段定义和方法定义,则被编译到主构造函数中
可以用_
对成员初始变量进行初始化,对应为0或者null
class C(var x: R) {
assert(x > 0, "positive please") //对参数的合法性进行检验
var y = x
val readonly = 5
private var secret = _
def this() = this(42)
}
assert 方法位于
scala.Predef
对象内
辅助构造函数
主构造函数之外的构造函数称为辅助构造函数,辅助构造函数的第一个条语句必须是调用同类的其他构造函数,所有每个Scala构造器最终都会调用主构造器
def this() = this(42)
私有构造函数
在类名称和参数列表之间添加关键字private/protected
,来声明私有的或受保护的构造函数
class C private (var x: R)
隐式转换
implicit
修饰符告诉编译器在某些情况下自动应用它,它必须定义在作用域范围之内
implicit def intToRational(x: Int) = new Rational(x)
创建一个隐式转换,在需要时自动将整数转换为有理数
抽象类
使用abstract
来声明
abstract class D { ... }
继承
子类拥有父类非private的方法,如果不显示指明继承,默认继承scala.AnyRef
,与java.lang.Object
等同
class C extends D { ... }
方法重载
override
关键词,进行函数重载,如果子类重写了父类的具体函数,必须添加override
修饰符,否则就会出现编译错误
在Scala中,字段和方法属于同一个名称空间,可以使用字段覆盖无参数的方法
abstract class Element {
def contents: Array[String]
}
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
Java有四个名称空间是字段,方法,类型和包,相比之下,Scala有两个命名空间namespace:
- 值(字段,方法,包和单例对象)
- 类型(类和特质名称)
因此可以用字段覆盖无参数的方法,同一类内部的字段和方法名称不能相同
调用超类的构造函数
传递参数给超类的主构造函数,把参数放到超类名称后的括号内即可
class D(var x: R)
class C(x: R) extends D(x)
参数化字段 parametric field
一种简写方式,用于组合参数和字段,同时定义同名的参数和字段
//这里的conts只是构造函数的参数,类没有对应的字段
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
// 简写,自动生成同名的字段
class ArrayElement(
val contents: Array[String]
) extends Element
参数前可以是val
,var
,private
,protected
,overried
之类的修饰符
class Cat {
val dangerous = false
}
class Tiger(
override val dangerous: Boolean,
private var age: Int
) extends Cat
//等价于
class Tiger(param1: Boolean, param2: Int) extends Cat {
override val dangerous = param1
private var age = param2
}
实际上如在出构造函数之外的地方使用构造参数,内部依然会把它转换为private val
类型的字段
class Foo(bar: Int){
def funtion1(): Unit ={
println(bar)
}
}
内部包含字段private val bar: Int
,与class Foo(private val bar: Int)
的区别在于没有对应的私有getter方法private int bar();
生成
变量的Getter/Setter方法
Scal字段会自动生成getter和setter方法,val
类型没有setter方法
Scala支持setter方法的名称为getter函数名后加_=
,使用时可以对getter函数名直接赋值
class Test{
private var _x = 0
def x = _x //get方法
def x_= (newValue: Int): Unit = { //set方法
_x = newValue
}
}
//使用方法
val test = new Test
test.x = 5
val x = test.x
Singleton Objects
Scala没有static
成员,通过单例对象来实现类似功能,等同于把所有的静态成员都放到了单例对象中,可以通过名称直接调用它的方法,就像一个静态方法的集合
- 它的定义类似于一个类的定义,不同的是它的关键字是
object
- object是只有一个实例的类,本身可以看做是一个惰性加载的
val
类型 - 实际实现是一个合成类(synthetic class)实例,被类成员中的一个静态变量引用,编译产生的类名是对象名称加上美元符号
SingletonObjectName$
单例对象和类的一个不同在于单例对象不能接收参数,而类可以。因为不能使用 new 来实例化一个单例对象,因此没有办法给它传递参数
Companion object
单例对象的名字和类名称一致的时候叫做伴生对象companion object
,必须在同一个文件中定义类和它的伴生对象,他们可以互相访问对方的私有成员
standalone object
没有和类共享名称的单例对象叫做独立对象,为了运行一个Scala程序,必须提供一个独立对象,内部包含一个程序执行的入口函数def main(args: String) = {}
Scala每个源文件都默认包含
java.lang
和scala
包,以及scala.Predef
的单例对象
Predef
中包含了许多有用的方法, 例如,println
和assert
方法
同时Scala不要求.scala
文件名称对应内部的类,可以随意命名
通过混入scala.App
类型的trait
,定义应用程序对象,即在对象后添加extends App
,放置在main方法中的代码直接放在单体对象的花括号之间,通过args
字符串数组访问命令行参数即可
特质trait
封装字段和方法定义,类似Java的接口,使用extends
或者with
关键字,混入(mixin
)到类中,数量没有限制
使用extends
关键词,类隐式继承特质的超类(特质没有声明超类时,默认为AnyRef
)
如果类已经显示扩展超类,使用with
- trait不能有类参数(传递到类的主构造函数的参数)
- 超类调用是不确定的,动态变化的(trait超类的调用方式)
trait T1; trait T2
class C extends T1 with T2
class C extends D with T1 with T2
特质不仅可以有抽象方法,也可以包含具体方法
trait Ordered[A] extends Any with java.lang.Comparable[A] {
def compare(that: A): Int
def < (that: A): Boolean = (this compare that) < 0
def > (that: A): Boolean = (this compare that) > 0
def <= (that: A): Boolean = (this compare that) <= 0
def >= (that: A): Boolean = (this compare that) >= 0
def compareTo(that: A): Int = compare(that)
def compareTo(that: A): Int = compare(that)
}
stackable modification 可堆叠的变化
特点
- 特质指定超类,那么只能用于同样扩展该超类的类
- 特质的抽象方法调用超类,特质中的super调用是动态绑定的,混入另一个特征或类后,才有具体的定义
abstract override
修饰符组合,只能由于特质,它表明特质必须混入到有具体方法定义的类中
abstract class IntQueue {
def get(): Int
def put(x: Int)
}
import scala.collection.mutable.ArrayBuffer
class BasicIntQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x: Int) = { buf += x }
}
trait Doubling extends IntQueue {
abstract override def put(x: Int) = { super.put(2 * x) }
}
trait Incrementing extends IntQueue {
abstract override def put(x: Int) = { super.put(x + 1) }
}
trait Filtering extends IntQueue {
abstract override def put(x: Int) = {
if (x >= 0) super.put(x)
}
}
val queue = (new BasicIntQueue with Filtering with Incrementing)
多个traits重载同一个方法,不同的次序会产生不同的类,优先级是从右向左,以类似栈的形式调用函数
类似多重继承,是利用线性化linearization算法解释超类
当用new实例化一个类时,Scala把这个类及其所有继承的类和特质以线性次序放在一起。 然后,每当其中一个类中调用super时,被调用的方法就是链中的下一个。 如果除最后一次调用之外的所有方法都调用super,那么最终结果就是是可堆叠行为