Scala之主构造函数
推荐:博主历时三年倾注大量心血创作的《大数据平台架构与原型实现:数据中台建设实战》一书已由知名IT图书品牌电子工业出版社博文视点出版发行,真诚推荐给每一位读者!点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,扫码进入京东手机购书页面!
##主构造函数
首先,我们必须要非常清晰明确的认识到:主构造函数不是你看到的class后面跟的参数列表,那怎么可能是主构造函数呢?那只是主构造函数的函数列表!那主构造函数的函数体在那里呢?答案是:class body里所有除去字段和方法声明的语句,剩下的一切都是主构造函数的,它们在class实例化时一定会被执行。本文原文出处: http://blog.csdn.net/bluishglc/article/details/50899077 严禁任何形式的转载,否则将委托CSDN官方维护权益!
所以说,Scala的主构造函数包含这些部分:
- The constructor parameters
- Methods that are called in the body of the class
- Statements and expressions that are executed in the body of the class
请看实例代码中的class Person1以及它的输出。从这个例子上我们可以看出:主构造函数看上去和类的定义已经完全融合在了一起!它的参数列表放到了类名的后面(我们也可以直接叫它类参数列表),它的方法体就是整个类体,实例化一个类时,类体(主构造函数)中所有可行的部分都会被执行,不管是函数调用还是表达式等等,只是对于类的字段和方法声明而言是没有什么可执行的,它们只是声明而已。
##主构造函数的参数(类参数)
首先,我们还是要非常清晰明确的认识到:
- 在主构造函数的参数列表中声明的参数和在类体中声明的变量本质上没有任何不同!基于前面我们对主构造参数的理解,这就像是:在函数的参数列表中声明的参数和方法体中声明的变量本质上没有任何不同一样!
那么接下来的问题就是要讨论不管是主构造函数列表中的参数还是类体中的字段,它们的可见性到底怎样定义?
首先,要说明的是:var/val限定的是变量是否可读写,与可见性无关,即对外可见;public和private声明的才是可见性,但是在省略这些关键字时还是有默认值的,情况如下:
- 如果只有var/val修饰,则字段总是对外可见的,即默认是public的
- 如果连var/val也没有,则字段对外不可见,同时内部只读,不可改写,即默认是:private val
- 第二条不适用于case class,case class的类参数在不指定val/var修饰时,会自动编译为public val,即对外只读,如果需要case class的字段外部可写,可以显式地指定var关键字!
规则如下:
- 对于var修饰的参数:外部可读/可改写 (实际上是:编译器为该类参数(字段)自动生成了getter和setter)
- 对于val修饰的参数:外部可读/不可改写(实际上是:编译器为该类参数(字段)只生成了getter没有生成setter)
- 对于private var修饰的参数:内部可读/可改写 (编译器不会为私有类参数(字段)自动生成getter和setter)
- 对于private val修饰的参数:内部可读/不可改写 (编译器不会为该类参数(字段)自动生成getter和setter)
对于主构造函数的参数列表中声明的参数和在类体中声明的变量是否地位一致以及它们的可见性,请看实例代码中的class Person2和Person3以及它们的输出。
##类字段的getter/setter
虽然通过val/var,我们已经可以轻松地指定字段的外部可访问性了,但是我们还是要清楚的知道under the hood! 也就是说在scala里getter和setter是怎么写的,虽然绝大多数情况下我们不会再像java那样去手动地编写getter与setter了。
示例代码中的Person4给我们一个很好的示例。虽然参数_age被声明为了私有的,但是通过手动的添加getter和setter,我们还是一样可以在外部改写他们。
##示例代码
object PrimaryConstructorDemo {
//Person1 is to show: primary constructor consists of not only class args list but also all runnable part in class body.
class Person1(var firstName: String, var lastName: String) {
println("the constructor begins")
// some class fields
private val HOME = "/root"
var age = 30
// some methods
override def toString = s"$firstName $lastName is $age years old"
def printHome { println(s"HOME = $HOME") }
def printFullName { println(this) } // uses toString
printHome
printFullName
println("still in the constructor")
}
// Person2 is to show: the visibility of class fields
class Person2 {
var age = 30
val gender = "male"
private val healthy = false
}
// Person3 is to show: the visibility of primary constructor args
class Person3(var age:Int,val gender:String,private val healthy:Boolean)
// Person4 is to show: change visibility for primary constructor args
class Person4(private var _age:Int) {
def age = _age // this is getter
def age_=(newAge: Int) = _age = newAge //this is setter
}
def main(args: Array[String]) {
val p1 = new Person1("Tome","White")
println("---------------------------------")
val p2 = new Person2
println(p2.age)
p2.age = 40;
println(p2.age)
println(p2.gender)
//error, no setter for gender.
//p2.gender = false
//error, invisible out of class.
//println(p2.healthy)
println("---------------------------------")
val p3 = new Person3(30,"male",false)
println(p3.age)
p3.age = 40;
println(p3.age)
println(p3.gender)
//error, no setter for gender.
//p3.gender = false
//error, invisible out of class.
//println(p3.healthy)
println("---------------------------------")
val p4 = new Person4(30)
println(p4.age)
p4.age = 40
println(p4.age)
}
}
程序输出:
the constructor begins
HOME = /root
Tome White is 30 years old
still in the constructor
---------------------------------
30
40
male
---------------------------------
30
40
male
---------------------------------
30