一、初识类和对象
Scala的类与Java的类具有非常多的相似性,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| class Person {
private var age = 0
private var name: String = _
def growUp(step: Int): Unit = { age += step }
def growUpFix(): Unit = { age += 10 }
def currentAge: Int = { age }
def getName: String = name
}
object Person {
def main(args: Array[String]): Unit = { val counter = new Person() counter.age = 12 counter.growUp(8) counter.growUpFix() println(counter.age) println(counter.currentAge) println(counter.getName) }
}
|
二、类
2.1 成员变量可见性
Scala中成员变量的可见性默认都是public,如果想要保证其不被外部干扰,可以声明为private,并通过getter和setter方法进行访问。
2.2 getter和setter属性
getter和setter属性与声明变量时使用的关键字有关:
- 使用var关键字:变量同时拥有getter和setter属性;
- 使用val关键字:变量只拥有getter属性;
- 使用private[this]:变量既没有getter属性、也没有setter属性,只能通过内部的方法访问;
需要特别说明的是:假设变量名为age,则其对应的get和set的方法名分别叫做 age
和age_=
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Person {
private val name = "myhhub" private var age = 12 private[this] var birthday = "2019-08-08" def getBirthday: String = birthday }
object Person { def main(args: Array[String]): Unit = { val person = new Person person.age = 30 println(person.name) println(person.age) println(person.getBirthday) } }
|
解释说明:
示例代码中person.age=30
在执行时内部实际是调用了方法person.age_=(30)
,而person.age
内部执行时实际是调用了person.age()
方法。想要证明这一点,可以对代码进行反编译。同时为了说明成员变量可见性的问题,我们对下面这段代码进行反编译:
1 2 3 4
| class Person { var name = "" private var age = "" }
|
依次执行下面编译命令:
1 2
| > scalac Person.scala > javap -private Person
|
编译结果如下,从编译结果可以看到实际的get和set的方法名(因为JVM不允许在方法名中出现=,所以它被翻译成$eq),同时也验证了成员变量默认的可见性为public。
1 2 3 4 5 6 7 8 9 10 11 12 13
| Compiled from "Person.scala" public class Person { private java.lang.String name; private java.lang.String age; public java.lang.String name(); public void name_$eq(java.lang.String); private java.lang.String age(); private void age_$eq(java.lang.String); public Person(); }
|
2.3 @BeanProperty
在上面的例子中可以看到我们是使用.
来对成员变量进行访问的,如果想要额外生成和Java中一样的getXXX和setXXX方法,则需要使用@BeanProperty进行注解。
1 2 3 4 5 6 7 8 9 10 11
| class Person { @BeanProperty var name = "" }
object Person { def main(args: Array[String]): Unit = { val person = new Person person.setName("myhhub") println(person.getName) } }
|
2.4 主构造器
和Java不同的是,Scala类的主构造器直接写在类名后面,但注意以下两点:
- 主构造器传入的参数默认就是val类型的,即不可变,你没有办法在内部改变传参;
- 写在主构造器中的代码块会在类初始化的时候被执行,功能类似于Java的静态代码块
static{}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Person(val name: String, val age: Int) {
println("功能类似于Java的静态代码块static{}")
def getDetail: String = { name + ":" + age } }
object Person { def main(args: Array[String]): Unit = { val person = new Person("myhhub", 20) println(person.getDetail) } }
输出: 功能类似于Java的静态代码块static{} myhhub:20
|
2.5 辅助构造器
辅助构造器有两点硬性要求:
- 辅助构造器的名称必须为this;
- 每个辅助构造器必须以主构造器或其他的辅助构造器的调用开始。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Person(val name: String, val age: Int) {
private var birthday = ""
def this(name: String, age: Int, birthday: String) { this(name, age) this.birthday = birthday }
override def toString: String = name + ":" + age + ":" + birthday }
object Person { def main(args: Array[String]): Unit = { println(new Person("myhhub", 20, "2019-02-21")) } }
|
2.6 方法传参不可变
在Scala中,方法传参默认是val类型,即不可变,这意味着你在方法体内部不能改变传入的参数。这和Scala的设计理念有关,Scala遵循函数式编程理念,强调方法不应该有副作用。
1 2 3 4 5 6 7
| class Person() {
def low(word: String): String = { word="word" word.toLowerCase } }
|
三、对象
Scala中的object(对象)主要有以下几个作用:
- 因为object中的变量和方法都是静态的,所以可以用于存放工具类;
- 可以作为单例对象的容器;
- 可以作为类的伴生对象;
- 可以拓展类或特质;
- 可以拓展Enumeration来实现枚举。
3.1 工具类&单例&全局静态常量&拓展特质
这里我们创建一个对象Utils
,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| object Utils {
val person = new Person val CONSTANT = "固定常量"
def low(word: String): String = { word.toLowerCase } }
|
其中Person类代码如下:
1 2 3
| class Person() { println("Person默认构造器被调用") }
|
新建测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| object ScalaApp extends App {
println(Utils.person == Utils.person)
println(Utils.CONSTANT)
println(Utils.low("ABCDEFG")) }
Person默认构造器被调用 true 固定常量 abcdefg
|
3.2 伴生对象
在Java中,你通常会用到既有实例方法又有静态方法的类,在Scala中,可以通过类和与类同名的伴生对象来实现。类和伴生对象必须存在与同一个文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Person() {
private val name = "myhhub"
def getName: String = { Person.toLow(Person.PREFIX + name) } }
object Person {
val PREFIX = "prefix-"
def toLow(word: String): String = { word.toLowerCase }
def main(args: Array[String]): Unit = { val person = new Person println(person.getName) }
}
|
3.3 实现枚举类
Scala中没有直接提供枚举类,需要通过扩展Enumeration
,并调用其中的Value方法对所有枚举值进行初始化来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| object Color extends Enumeration {
type Color = Value
val GREEN = Value val RED = Value(3) val BULE = Value("blue") val YELLOW = Value(5, "yellow") val PINK = Value }
|
使用枚举类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import com.myhhub.Color.Color
object ScalaApp extends App {
def printColor(color: Color): Unit = { println(color.toString) }
println(Color.YELLOW.toString == "yellow") for (c <- Color.values) println(c.id + ":" + c.toString) }
true 0:GREEN 3:RED 4:blue 5:yellow 6:PINK
|
参考资料
- Martin Odersky . Scala编程(第3版)[M] . 电子工业出版社 . 2018-1-1
- 凯.S.霍斯特曼 . 快学Scala(第2版)[M] . 电子工业出版社 . 2017-7