1. 程式人生 > >Scala學習(一)——基礎語法

Scala學習(一)——基礎語法

extend property obj array add 活性 devel type 單個

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學習(一)——基礎語法