1. 程式人生 > >scala基本語法學習之一

scala基本語法學習之一

scala與java有許多相似地方,scala是面向物件與面向過程程式設計的統一體,java在jdk8之前都是面向物件程式設計。因此scala具有面向物件程式設計的特點,即封裝、繼承和多型,又有面向過程的特點,使得scala語言更加靈活多變,掌握起來有一定的難度。scala編譯器的作者也是java編譯器的作者,因此scala能夠執行在所有的jvm上,scala具有與java一樣的跨平臺特徵。

本文整理scala語法,供學習使用。

一、基礎

1.表示式。    表示式是可以運算的句子。

  你可以使用println或print打印表達式的結果。例如println(1);println(1+1);println(“hello”)等等

  你可以為表示式的結果起一個名字,使用val關鍵字修飾。這個在某種情況下類似於java中的常量,其值不可更改。例如val x = 1+1;val x : String = "hello".

  你可以將表示式結果賦給一個變數。變數與常量不同之處在於變數的值可以變化。例如var x = 1+1; var s:String = "Hello"等

2.塊。    你可以將多句表示式使用{}包起來,形成一語句塊,scala中稱為block。例如println({

val x = 1+1

x+1

})

3.函式。   函式也是表示式,是帶有引數的表示式。例如匿名函式(x:Int,y:Int) => x+y,=>左邊是引數列表,注意引數必須制定型別,右邊是表示式或block。要呼叫這個匿名函式,需要為其起一個名字,這個名字可以用val或var修飾。

例如val sum = (x:Int,y:Int) => x+y;var sum = (x:Int,y:Int) => x+y。呼叫時就是sum(1,2)。

4.方法。方法與函式相似,但是是使用關鍵字def來定義的。

例如def sum(x:Int,y:Int) : Int = x+y。例子中def後面跟的是方法名、引數列表(沒有可以省略)、返回值型別(預設是Unit,可以省略)、方法體。如果沒有設定返回值型別‘=’就不需要了,沒有引數列表“()”可以不要。

方法可以有多個引數列表,也可以不帶引數列表。方法體可以是一句表示式,也可以是一個block。方法體的最後一行表示式的結果就是該方法的返回值。scala有return關鍵字,但是很少使用。

5.class。    scala的類使用class關鍵字定義。scala類的建構函式是跟類的定義一起的,類名後可以跟一個建構函式引數列表,也可以不帶引數列表,預設是無參的建構函式。

例如class Sum(x:Int, y:Int) {

println("This is main constructor.")

def this(x:Int, y:Int, z:Int){

this(x,y)

println("this is assistant constructor.")

}

}

scala的建構函式分為主建構函式和輔助建構函式。主建構函式與類的定義統一在一起。輔助建構函式函式名必須是this關鍵字,且第一行語句必須使用this關鍵在呼叫主建構函式或其他輔助建構函式。

類的例項化使用new關鍵字。不帶引數的建構函式,在例項化時()可以省略。

注意scala中方法的引數列表都是val的,因此在方法中使用時是不可以更改其值的。但是主建構函式的引數列表可以定義成var,使用var修飾引數就行,方法及輔助建構函式的引數列表是不可以使用var修飾的。

6.case  class。case class是一種特殊的類,這種類使用case class兩個關鍵字修飾的,且類的主建構函式的引數列表為空時,()也不能省略。    case class的主建構函式的引數列表預設是val的,即不可變值,雖然你可以指定其為var,但是這樣做是不推薦的。case class設計的目的是建立一個不變類,它在編譯時會自動新增一些通用的方法,例如toString,copy,hashcode,equals等等。case class在編譯時,編譯器會建立一個伴生類,它的作用是在建立case class的例項時可以不使用new關鍵字,但這僅限於使用主建構函式建立例項。除了伴生類與自動建立一些通用方法外,case class與普通class沒什麼不同。一般case class用於模式匹配,值比較。通常case class只有類的定義,沒有類體, 例如case class A(x:String, y:String),建立例項val a= A("x", "y")。

7.object。object是一種特殊類,類似單例, 使用object關鍵字修飾。但是與java單例不同的是,它不允許有輔助建構函式,它的主建構函式不允許帶引數。因此objce類其實是一些靜態方法的集合。

例如object A {

def doSomething{

println("This is an object.")

}

}

呼叫方式A.doSomething即可,你也可以將A取個名字a,但a只能用val修飾,如果使用var修飾的話使用a呼叫A的方法時會報錯。

8.trait。trait是一種特殊類,它有點類似java的抽象類,java的抽象類是單繼承特性,trait也是單繼承特徵。但是java有interface可以擴充套件子類的功能,scala在不破壞單繼承特性基礎上,添加了組合trait的特徵,關鍵字是with。trait的抽象方法可以有預設的實現。trait不允許有建構函式,因此它的類名是不允許帶引數列表的,所以trait是不能夠直接例項化的。

例如trait A{

def doSomething{

var x:Int

val y=0

var s="default"

println("this is default implement of A")

}

def printSomething

}

trait B{

def printSomething

}

子類在繼承trait類時使用extends關鍵字,如果想實現多個trait的屬性或方法,可以使用with關鍵字將多個trait變成一個組合trait。

子類class C extends A with B{

override var x:Int = 0

override def doSomething{

println("this is implement of C")

}

def printSomething{

println(s)

}

}

從上面可以看到,子類可以繼承trait的方法及屬性,如果trait中屬性沒有被初始化,則子類中使用時需要對其進行初始化。如果trait中方法有預設實現,則子類中想重寫的話需要使用override關鍵字宣告該方法是重寫父類方法。如果多個trait中有同樣結構的方法,則子類中需要override這個方法。如果trait中定義的變數有初始化,則子類中不能重新定義該變數。

9.main方法。main方法是scala應用的入口,這是因為scala使用的是jvm,jvm要求應用的入口方法必須是名字為main的方法,且沒有返回值,引數是一個String陣列。scala中main方法必須寫在objcet類中。

二、型別層次結構

與java類似,scala中所有的值或函式都有型別。所有的型別都有一個基類Any,同時所有的型別都有一個子類Nothing,後面一點是與java不同的地方。scala的型別層次結構如下圖

從上圖中可以看出Ay有兩個直接的子類AnyVal及AnyRef。AnyVal有點類似java的基本資料型別,AnRef類似java的Object類,我們所寫的scala類均是AnyRef的子類。

AnyVal代表了值型別。scala提供了9中預定義的AnyVal型別。值型別是不能為null的。這9中值型別是Double,Float,Long,Int,Short,Byte,Char,Boolean,Unit。Unit表示無意義的資訊,是函式和方法的預設返回型別,類似java的void返回型別,你可以為一個變數初始化一個值(),()就是宣告一個Unit型別。

AnyRef表示引用型別,一切非值型別都是引用型別。

與java不同的是scala的型別層次中有個Nothing型別,這個型別不是任何值的型別,它是一切型別的子類,但你不能建立和使用這個型別,它唯一的作用是給出一個沒有結束的訊號,例如發生異常、程式退出、或者是出現了無限迴圈問題時,它被用來表示無法獲取一個值或者方法沒有正常返回。。

Null型別是所有引用型別的子類,即所有AnyRef的子類。Null裡只有一個null值,如果你宣告一個變數的型別是Null,那麼它的值只能是null。Null的主要作用是用來與其他jvm語言進行協同工作的,在scala程式碼裡基本不使用。

Nothing與Null型別均無法獲取例項。

三、型別轉換

值型別可以按照下面方式進行轉換

從圖中可以看出它遵循的是小轉大,即所佔位元組小的可以向位元組大的值型別轉換。值型別轉換是單向的,Boolean型不能參與型別轉換。這些與java的基本資料型別向上轉型一致。

四、訪問控制符

scala有public、protected、private三種修飾符。不使用修飾符預設是public,這點與java不同,java不使用修飾符預設是package。scala的修飾符均可以修飾類名,這點與java也不同,java裡修飾類名只能是public或者不寫。

scala訪問控制符作用的物件是包內成員,類或物件內成員(包括成員屬性或成員方法)。

包內的成員即是類或物件,即class或object。public修飾class或object時,表示該class或object對一切物件或類是可見的。

其中,protected與provide用於修飾類名或物件名時,效果一樣,都是表示該包內及包的子目錄裡所有類或物件能夠訪問該類或物件。但包外的類或物件就無法看到該類或物件。

類或物件的成員包括屬性和方法。public修飾類或物件的成員表示所有能夠呼叫該類或物件的一切類或物件可以呼叫。

使用provide修飾類的成員時,scala又比java更為嚴格。即類或物件的內部類的成員如果是私有的,則外部類是無法呼叫內部類的私有成員的。

使用protected修飾類的成員時,scala比java更為嚴格。即類或物件的成員如果是受保護的,那麼他們只能夠被改類或物件及其子類訪問。

五、訪問控制符新增作用域

這個特點是java裡沒有的,訪問控制符本身規定了被修飾的成員的作用範圍,scala又對訪問控制符做了新的用法。

provide[x]及protected[x]

修飾符中括號裡的引數x是包名、類或物件名。它表示該修飾符的作用範圍到x包為止,就是說被這樣修飾的成員,它能夠被x包或類或物件的所有成員訪問,出了x包則就是provide或protected的。

六、行結束符

scala的行結束符比較靈活,它可以以‘;’作為結束符,也可以省略分號。如果一句程式碼需要佔用多行,則需要在每行開始前使用‘|’符。一行類如果有多條語句,必須要使用分號了,但是不建議這樣做,這樣做很不友好。scala風格一般是不使用分號做行結束符。

七、識別符號

scala的識別符號比java有很大不同。主要有四類識別符號:字元數字識別符號、符號識別符號、混合識別符號、字面量識別符號。

1.字元數字識別符號

此類識別符號與java類似,命名規則也遵循java的駝峰規則。不同之處在與,$符號是scala內建關鍵字,最好在識別符號中不要使用,以避免衝突。且最好不要以_結尾,以避免衝突。

2.符號識別符號

這類識別符號由有ASCII碼的特殊符號組成,例如+,-,*,/,?,#,*等等。這類識別符號在編譯時,scala會將其轉換成以$開頭的對應的英文。例如識別符號->就被編譯成$minus$greater。這類識別符號可以用於做方法名,當java程式碼要呼叫這類識別符號命名的方法時,只能呼叫他們編譯後的名字。

3.混合識別符號

混合識別符號是字元數字識別符號後面跟多個符號表字元組成。其中字元數字識別符號以_結尾。

4.字面量識別符號

文字識別符號是以``包括起來的一串隨意的字串。

八、關鍵字

abstract case catch class
def do else extends
false final finally for
forSome if implicit import
lazy match new null
object override package private
protected return sealed super
this throw trait try
true type val var
while with yield  

scala的關鍵字比java要少許多,但是scala執行在jvm上,因此對於缺少的關鍵字,scala都提供了替代的方法。例如instanceof關鍵字在scala中被isInstanceOf方法替代,它是scala的內建方法。

十、運算子

scala包括算術運算、邏輯運算、位運算、賦值運算、關係運算等幾種運算子。

1.算術運算子

有+、-、*、/、%幾種算術運算子。scala裡沒有自增++和自減--運算子。

2.邏輯運算子

有&&、||、!幾種種邏輯運算子。

3.關係運算符

有==、!=、>、<、>=、<=幾種關係運算符。

4.位運算子

有&、|、~、^、>>、<<、>>>等幾種。

5.賦值運算子

有=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=等幾種。

運算子的優先順序與java類似。