Spark(九)Spark源码阅读之Scala构造器
一、主构造器
组成
Scala类的主要构造函数是以下的组合:
1、构造函数参数
2、在类的主体中调用的方法
3、语句和表达式在类的主体中执行
// scala
class Person(var firstName: String, var lastName: String) { // primary constructor
println("the constructor begins")
// some class fields
private val HOME = System.getProperty("user.home")
var age = 0
// some methods
override def toString = s"$firstName $lastName is $age years old"
def printHome { println(s"HOME = $HOME") }
def printFullName { println(this) } // uses toString
// some expression
printHome
printFullName
println("still in the constructor")
}
object Demo {
def main(args: Array[String]): Unit = {
var instant = new Person("jing", "su")
}
}
Scala中,每个类都有主构造器,不需要单独声明构造函数,不以this方法定义,主构造器与类的定义或者字段声明交织在一起。当你阅读一个Scala类时,你需要将它们分开理解,一个是类的定义,一个是构造函数的定义。
主构造器的参数直接放置在类名之后,并直接被编译成字段,其值被初始化成构造时传入的参数。
在本例中lastname和firstname成为Person类的字段。
这样的构造器相比于Java代码,节约了极大的工作量。
-
无参主构造器
如果类名之后没有参数,则该类具备一个无参主构造器。这样一个构造器仅仅是简单地执行类体中的所有语句而已。你通常可以通过在主构造器中使用默认参数来避免过多地使用辅助构造器。例如:
class Person(val firstName: String = "", val lastName: String = "")
-
主构造器参数
主构造器的参数可以采用下表中列出的任意形态
image.png
例如:
class Person (val firstName: String, privite var lastName: String)
这段代码将声明并初始化如下字段:
val firstName: String
private var lastName: String
构造参数也可以是普通的方法参数,不带val或var。这样的参数是不可变得,而且不带修饰符的参数和private val还是有区别的。
他们的存在方式取决于它们在类中如何被使用。
class Person(private val firstName: String, lastName: String)
对于如上的类,将之编译并用javap -v Person查看Java字段之后,我们就可以发现仅仅只有firstName字段。而lastName仅仅是构造器参数,在构造完成之后,就会被垃圾回收。
如果不带val或var的参数至少被一个方法所使用,它将被编译器自动升格为字段。例如:
class Person(private val firstName: String, lastName: String) {
def fullName = firstName + " " + lastName
}
// if we'd defined fullName as a val instead of a def,
// it'd only have one field
如果我们将firstName声明为对象私有(object-private),而不是类私有(class-private),那么它和无修饰符的参数作用类似。具体可以查阅参考文献[5]的5.2章节。
class Person(private[this] val firstName: String, lastName: String)
class Person(private[this] var firstName: String, lastName: Strin
-
主构造器参数生成字段
下表总结了不同类型的主构造器参数对应会生成的字段和方法:
二、辅助构造器
除了主构造器之外,类还可以有任意多的辅助构造器(auxiliary constructor)。
我之所以后讨论辅助构造器,是因为主构造器更重要也更难理解。当明确地理解了Scala主构造器和Java、C++等构造函数的区别后,就能更加轻松地理解辅助构造器,因为辅助构造器同Java或C++的构造函数十分相似,只有两处不同。
1、辅助构造器的名称为this。而在Java或C++中,构造器的名称和类名相同。
2、每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开
class Person {
private var name = ""
private var age = 0
def this(name: String) { // 辅助构造器1
this() // 调用主构造器
this.name = name
}
def this (name: String, age: Int) { // 辅助构造器2
this(name) //调用辅助构造器1
this.age = age
}
}
object Testprivate {
def main(args: Array[String]): Unit = {
var instant1 = new Person //主构造器
var instant2 = new Person("sujing") //辅助构造器1
var instant3 = new Person("sujing", 3) //辅助构造器2
}
}
嵌套类
Scala内嵌类
在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。你可以在函数中定义函数,在类中定义类。以下代码是在类中定义类的一个示例:
class Network {
private val members = new ArrayBuffer[Member]
def join(name: String) = {
val m = new Member(name)
members += m
m
}
class Member(val name: String) {
val contacts = new ArrayBuffer[Member]
}
}
在Scala中,每个实例都有它自己的Member类,就和它们有自己的members字段一样,考虑有如下两个网络:
val chatter = new Network
val myFace = new Network
也就是说,chatter.Member和myFace.Member是不同的两个类。
这和Java不同,在Java中内部类从属于外部类。Scala采用的方式更符合常规,举例来说,要构建一个新的内部对象,你只需要简单的new这个类名:new chatter.Member。而在Java中,你需要使用一个特殊语法:chatter.new Member()。拿我们的网络示例来讲,你可以在各自的网络中添加成员,但不能跨网添加成员:
val fred = chatter.join("Fred")
val wilma = chatter.join("Wilma")
fred.contacts += wilma //OK
val barney = myFace.join("Barney") // 类型为myFace .Member
fred.contacts += barney // 不可以这样做,不能将一个myFace.Member添加到chatter.Member元素缓冲当中
Scala内嵌类访问
对于社交网络而言,这样的行为是讲得通的。如果你不希望是这个效果,有两种解决方式。
首先,你可以将Member类移到别处,一个不错的位置是Network的伴生对象。
object Network {
class Member (val name: String) {
val contacts=new ArrayBuffer[Member]
}
}
class Network {
private val members = new ArrayBuffer[Network.Member]
}
或者,你也可以使用类型投影Network#Member,其含义是“任何Network的Member”。例如:
class Network {
class Member (val name: String) {
val contacts = new ArrayBuffer[Network#Member]
}
}
内嵌类访问外部类
在内嵌类中,你可以通过外部类.this的方式来访问外部类的this引用,就像Java那样。
如果需要,也可以用如下语法建立一个指向该引用的别名:
class Network(val name: String){ outer=>
class Member (val name: String) {
def dascription=name+"inside"+outer.name
}
}