Scala學習(一)——基礎語法
Scala語言是一種面向對象語言,結合了命令式(imperative)和函數式(functional)編程風格,其設計理念是創造一種更好地支持組件的語言。
特性
-
多範式(Multi-Paradigm)編程語言,類似Java、C#;
-
繼承面向對象編程和函數式編程的特性;
-
面向對象:[1]. 子類繼承,[2]. 混入(Mixin)機制;
-
函數式:支持高階函數、嵌套函數,模式匹配,支持柯裏化(Currying);
-
類型推斷(Type Inference):根據函數體中的具體內容推斷方法的返回值類型
-
-
更高層的並發模型,可伸縮;
-
靜態類型,編譯時檢查;
-
允許與Java互操作(將來會支持與.Net互操作);
與Java對比
-
如果用作服務器端開發,Scala的簡潔、靈活性是Java無法比擬的;
-
如果是企業級應用開發、Web開發,Java的強大是Scala無法匹敵的;
基礎語法
表達式(Expressions)
表達式是可計算的語句。您可以使用println輸出表達式的結果。
println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!
值(Values)
您可以使用val關鍵字命名表達式的結果。
val x = 1 + 1
println(x) // 2
命名結果(例如x)稱為值。引用值不會重新計算它。值無法被重新分配。
val x = 1 + 1
x = 3 // This does not compile.
可以推斷出值的類型,但您也可以顯式聲明類型,如下所示:
val x: Int = 1 + 1
註意類型聲明Int如何在標識符x之後出現,你還需要一個“:”
變量(Variables)
變量就像值,除了你可以重新賦值。您可以使用var關鍵字定義變量。
var x = 1 + 1
x = 3 // This compiles because "x" is declared with the "var" keyword.
println(x * x) // 9
與值一樣,您可以根據需要明確說明類型:
var x: Int = 1 + 1
塊(Blocks)
您可以通過用{}包圍表達式來組合表達式。我們稱之為塊。塊中最後一個表達式的結果也是整個塊的結果。
println({
val x = 1 + 1
x + 1
}) // 3
函數(Functions)
函數是帶參數的表達式。
您可以定義一個返回給定整數加一的匿名函數(即無名稱):
(x: Int) => x + 1
在=>的左邊是參數列表。右邊是涉及參數的表達式。
您也可以命名函數。
val addOne = (x: Int) => x + 1
println(addOne(1)) // 2
函數可能需要多個參數。
val add = (x: Int, y: Int) => x + y
println(add(1, 2)) // 3
或者它不需要參數。
val getTheAnswer = () => 42
println(getTheAnswer()) // 42
方法(Methods)
方法的外觀和行為與函數非常相似,但它們之間存在一些關鍵差異。
方法使用def關鍵字定義。 def後跟名稱,參數列表,返回類型和正文。
def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3
註意如何在參數列表和冒號之後聲明返回類型:Int。
方法可以采用多個參數列表。
def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
println(addThenMultiply(1, 2)(3)) // 9
或者根本沒有參數列表。
def name: String = System.getProperty("user.name")
println("Hello, " + name + "!")
還有一些其他差異,但就目前而言,您可以將它們視為與函數類似的東西。
方法也可以有多行表達式。
def getSquareString(input: Double): String = {
val square = input * input
square.toString
}
正文中的最後一個表達式是方法的返回值。 (Scala確實有一個return關鍵字,但它很少使用。)
關於方法(Method)和函數(Function)
函數是一等公民,使用val語句可以定義函數,def語句定義方法;
函數是一個對象,繼承自FuctionN,函數對象有curried,equals,isInstanceOf,toString等方法,而方法不具有;
函數是一個值,可以給val變量賦值,方法不是值、也不可以給val變量賦值;
通過將方法轉化為函數的方式 method _ 或 method(_) 實現給val變量賦值;
若 method 有重載的情況,方法轉化為函數時必須指定參數和返回值的類型;
某些情況下,編譯器可以根據上下文自動將方法轉換為函數;
無參數的方法 method 沒有參數列表(調用:method),無參數的函數 function 有空列表(調用:function());
方法可以使用參數序列,轉換為函數後,必須用 Seq 包裝參數序列;
方法支持默認參數,函數不支持、會忽略之;
類(Classes)
您可以使用class關鍵字定義類,後跟其名稱和構造函數參數。
class Greeter(prefix: String, suffix: String) {
def greet(name: String): Unit =
println(prefix + name + suffix)
}
方法greet的返回類型是Unit,它表示返回沒有任何意義。它與Java和C中的void類似地使用。(不同之處在於,因為每個Scala表達式必須具有某個值,實際上存在Unit類型的單例值,write()。它不攜帶任何信息。)
您可以使用new關鍵字創建類的實例。
val greeter = new Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!
樣本類(Case Classes)
Scala有一種特殊類型的類,稱為樣本類。默認情況下,樣本類是不可變的,並按值進行比較。您可以使用“case class”關鍵字定義案例類。
case class Point(x: Int, y: Int)
您可以在沒有new關鍵字的情況下實例化樣本類。
val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)
並且它們按值進行比較。
if (point == anotherPoint) {
println(point + " and " + anotherPoint + " are the same.")
} else {
println(point + " and " + anotherPoint + " are different.")
}
// Point(1,2) and Point(1,2) are the same.
?
if (point == yetAnotherPoint) {
println(point + " and " + yetAnotherPoint + " are the same.")
} else {
println(point + " and " + yetAnotherPoint + " are different.")
}
// Point(1,2) and Point(2,2) are different.
對象(Objects)
對象是它們自己定義的單個實例。你可以把它們想象成自己班級的單身人士。
您可以使用object關鍵字定義對象。
object IdFactory {
private var counter = 0
def create(): Int = {
counter += 1
counter
}
}
您可以通過引用其名稱來訪問該對象。
val newId: Int = IdFactory.create()
println(newId) // 1
val newerId: Int = IdFactory.create()
println(newerId) // 2
抽象類
你可以定義一個抽象類,它定義了一些方法但沒有實現它們。取而代之是由擴展抽象類的子類定義這些方法。你不能創建抽象類的實例。
abstract class Shape {
def getArea():Int // subclass should define this
}
class Circle(r: Int) extends Shape {
def getArea():Int = { r * r * 3 }
}
val s = new Shape //error: class Shape is abstract; cannot be instantiated
val c = new Circle(2)
特質(Traits)
特質是包含某些字段和方法的類型。可以組合多種特質。
您可以使用trait關鍵字定義特質。
trait Greeter {
def greet(name: String): Unit
}
特質也可以有默認實現。
trait Greeter {
def greet(name: String): Unit =
println("Hello, " + name + "!")
}
您可以使用extends關鍵字擴展traits並使用override關鍵字覆蓋實現。
class DefaultGreeter extends Greeter
?
class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {
override def greet(name: String): Unit = {
println(prefix + name + postfix)
}
}
?
val greeter = new DefaultGreeter()
greeter.greet("Scala developer") // Hello, Scala developer!
?
val customGreeter = new CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala developer") // How are you, Scala developer?
在這裏,DefaultGreeter只擴展了一個特質,但它可以擴展多個特質。
什麽時候應該使用特質而不是抽象類?
如果你想定義一個類似接口的類型,你可能會在特質和抽象類之間難以取舍。這兩種形式都可以讓你定義一個類型的一些行為,並要求繼承者定義一些其他行為。一些經驗法則:
-
優先使用特質。一個類擴展多個特質是很方便的,但卻只能擴展一個抽象類。
-
如果你需要構造函數參數,使用抽象類。因為抽象類可以定義帶參數的構造函數,而特質不行。例如,你不能說trait t(i: Int) {},參數i是非法的。
Main方法
Main方法是程序的切入點。 Java虛擬機需要將main方法命名為main,並使用一個參數,即一個字符串數組。
使用對象,您可以按如下方式定義main方法:
object Main {
def main(args: Array[String]): Unit =
println("Hello, Scala developer!")
}
本文主要翻譯自Basics|Scala Documentation
Scala學習(一)——基礎語法