一、主從構造器
java中構造函數沒有主、從之分,隻有構造器重載,但在scala中,每個類都有一個主構造器,在定義class時,如果啥也沒寫,預設有一個xxx()的主構造器
class Person {
var name: String = _
/**
* 從構造器
* @param name
*/
def this(name: String) = {
this //注意:從構造器,必須先調用主構造器
this.name = name;
}
override def toString = {
"name:" + name
}
}
上面的這段代碼,如果将生成的class反編譯成java來看的話,可能更容易了解:
public class Person
{
private String name;
public String name()
{
return this.name; }
public void name_$eq(String x$1) { this.name = x$1; }
public String toString()
{
return new StringBuilder().append("name:").append(name()).toString();
}
public Person()
{
}
public Person(String name)
{
this();
name_$eq(name);
}
}
從反編譯結果看,生成了二個構造函數,一個是預設的Person(),另一個是Person(String name)。
Scala是一個崇尚簡約之美的語言,在定義Class時,可以将屬性聲明、帶參構造器,一并全解決了,是以剛才這段代碼,"等效"于下面這樣:
class Person(var name: String) {
override def toString = {
"name:" + name
}
}
注意第一行,此時的主構造器不再是預設的無參構造器,而是Person(var name:String),它有二層意思:一是定義了一個帶參數的構造器,二是由于name前加了var,說明name:String不僅僅是構造器的參數,還是類Person的一個屬性成員,不過這個版本與第一個版本還是有些差别的,如果用JD-GUI反編譯檢視的話,會發現預設的無參構造器消失了
public class Person
{
private String name;
public String name()
{
return this.name; }
public void name_$eq(String x$1) { this.name = x$1; }
public String toString() {
return new StringBuilder().append("name:").append(name()).toString();
}
public Person(String name)
{
}
}
Person的使用示例如下:
object App {
def main(args: Array[String]) {
val p: Person = new Person("jimmy")
println(p.toString)
p.name = "jimmy.yang"
println(p.toString)
}
}
主構造器上,還可以增加限定詞private來修飾,比如下面這樣:
class Person private(var name: String) {
var age: Int = 0;
def this(age: Int, name: String) = {
this(name)
this.age = age;
}
override def toString = {
"name:" + name + ",age:" + age
}
}
這樣Person類的使用方,就隻能使用從屬構造器this(age:Int,name:String)了。
二、私有屬性(private property)
将前面的Person改一下,将年齡Age設定成私有成員
package yjmyzz
class Person private(var name: String) {
println("這是主構造器的可執行語句,我是" + name) //這一行在new Person時會執行
/**
* 定義一個私有成員
*/
private var _age: Int = 0;
def age = _age
def this(age: Int, name: String) = {
this(name)
this._age = age;
}
def isOlder(another: Person) = {
this._age > another._age //注意:這裡可直接通路另一個Person的私有成員_age
}
override def toString = {
"name:" + name + ",age:" + age
}
}
注意:isOlder方法,該方法用于比較二個Person誰更年長,跟java不同的是,在Class定義範圍内,可以直接通路另一個類執行個體的私有成員!這在java、c#中是絕對不允許的。
另外,還有一個值得注意的地方,Class的定義裡,除了def定義的方法(或許稱為函數更适合)外,任何可執行語句都會被執行,比如第6行的println語句。下面是調用示例:
val jimmy: Person = new Person(18, "jimmy")
val mike: Person = new Person(30, "mike")
if (mike.isOlder(jimmy))
println(mike.name + " is older than " + jimmy.name)
執行結果:
這是主構造器的可執行語句,我是jimmy
這是主構造器的可執行語句,我是mike
mike is older than jimmy
如果不希望private成員在Class定義中直接被其它執行個體所通路,可以改成private[this],即:
private[this] var _age: Int = 0;
def age = _age
def isOlder(another: Person) = {
this._age > another.age
}
這樣的話,isOlder中的another,隻能通過函數age來通路私有成員_age了。
三、static成員/伴生對象Object/apply方法
scala裡并沒有static關鍵字,要達到類似的效果,可以借助object對象,object天然是singleton模式,比如下面的代碼:
object Singleton {
var count = 0;
def increment: Unit = {
count += 1
}
}
定義成object類型的對象,沒有辦法直接new, object中的所有方法都是靜态方法,這一點類似c#中的static靜态類,使用時直接按靜态方法調用即可:
var obj1 = Singleton.count
println(obj1)
Singleton.increment
var obj2 = Singleton.count
println(obj2)
object不僅僅用于單例模式的實作,更多時候,我們可以定義一個與class同名的object,然後把class的所有靜态方法放到object裡,比如:
class People(var name: String) {
println("main constructor in Class People")
}
object People {
def whoami: Unit = {
println("I am a people ")
}
def apply(name: String) = {
println("apply in Object People")
new People(name)
}
}
後面的object People稱為class People的伴生對象,可以了解為class People的靜态成員/方法集合,注意裡面的apply方法,這個方法會被自動調用,通常用于建立對象執行個體,有點工廠模式的意味,看下面的調用代碼:
var p:People = People("jimmy")
println(p.name)
People.whoami
這裡我們沒有用new關鍵字來建立對象,而是"隐式"調用了伴生對象的靜态方式apply,以下是輸出結果:
apply in Object People
main constructor in Class People
jimmy
I am a people
伴生對象+apply方法,這是scala中經常使用的一個技巧,即簡化了代碼,又起了工廠模式的作用,我們甚至還可以在apply方法中加入對象控制的額外業務邏輯,這比直接new對象更靈活。
從object的使用上,還可以看出靜态方法的調用上scala與java的不同,java中靜态方法即可以用"類名.靜态方法()",也可以用"對象執行個體.靜态方法()"來調用,說實話,有點不太講究,而Scala"糾正"了這一錯誤,靜态方法隻能在object(即:靜态類)上調用,非靜态方法隻能在對象執行個體上調用,這與c#的理念是一緻的(見:java學習:OOP入門 第7點)
apply方法不僅可以存在于object中,class中也可以有apply方法,我們把People的Class改一下:
class People(var name: String) {
println("main constructor in Class People")
def apply(): Unit ={
println("apply in Class People")
}
}
然後這麼調用:
var p:People = People("jimmy")
p()
注意第2行,就是這麼簡單!輸出結果:
apply in Class People
四、内部類(也稱嵌套類)
class内部還可以再定義類,即嵌套類,與java不同的是,scala的嵌套類是屬于執行個體的,而不屬于定義它的外部類。這句話聽着很繞,還是直接看代碼吧,先把前面的People類改造一下:
class People(var name: String) {
/**
* 定義嵌套類(注:必須寫在最開始,好象也隻能定義一個?)
*/
master =>
//這個master變量即指People本身this,名字可以随便取
class Pet(var name: String) {
def hi() = println("我叫" + this.name + " , 我是" + master.name + "的寵物!")
}
/**
* 增加了一個寵物屬性
*/
var pet: Pet = _
}
object People {
def apply(name: String, petName: String) = {
println("apply in Object People")
val people = new People(name)
people.pet = new people.Pet(petName)
people
}
}
然後使用:
val jimmy = new People("jimmy")
val dog = new jimmy.Pet("wang wang") //注:這是調用的"執行個體"上的Pet,而不是new People.Pet()
dog.hi()
println("------------")
val mike = People("Mike","miao miao")
mike.pet.hi()
println("------------")
//println("jimmy與mike交換寵物:")
//jimmy.pet = mike.pet //直接報錯,因為mike的寵物是隻屬于mike的,它與jimmy的寵物類型不相容
//jimmy又養了一隻貓
var cat = new jimmy.Pet("miao")
//然後把狗狗扔了
jimmy.pet = cat;
jimmy.pet.hi()
注意第2行及第13行,第2行是直接用 new 執行個體.内部類()的方式建立的,而非 new 外部類.内部類()這種方式,說明内部類是從屬于外部類的執行個體,第13行再次證明了這一點,雖然都是内部類Pet的執行個體,但當試圖将mike的Pet執行個體指派給jimmy的Pet執行個體時,編譯器直接報錯,說明内部類的執行個體一旦建立,則"生是X家人,死是X家鬼",絕對的忠貞不二。
作者:菩提樹下的楊過
出處:http://yjmyzz.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。