1. 程式人生 > 實用技巧 >scala之旅-核心語言特性基礎(二)

scala之旅-核心語言特性基礎(二)

本章將介紹scala語言的基礎內容

嘗試在瀏覽器中使用Scala

你可以使用ScalaFiddle在瀏覽器中執行Scala。這是一種簡單、零基礎的方式來實驗Scala程式碼段。

  1. 訪問網址https://scalafiddle.io.
  2. 複製
    println("Hello, world!")

    進左邊的面板

  3. 點選Run. 在右邊的面板就會顯示輸出

表示式

表示式是可以被計算的語句:

1+1

你可以用 println 對錶達式的結果輸出:

println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") //
Hello, world!

【注:輸出結果:】

1
2
Hello
Hello, world

你可以用關鍵字 val 來命名錶達式的結果

val x = 1 + 1
println(x) //2

【注:輸出結果:】

2

像 x 這種用來命名結果的,我們稱之為值。引用值不會重新計算表示式。

值不能被重新定義賦值. 【注:val修飾的變數是被final修飾過了】

x = 3 // 無法編譯通過.

值的型別描述可以被省略,編譯器會自行推斷出來。當然也可以顯式地宣告出來

val x: Int = 1 + 1

注意宣告型別時,Int是跟在命名 x 後的,用一個 : 表示這個值的型別

變數

變數跟值類似,但是變數可以重新賦值定義。你可以通過 var 關鍵字來宣告一個變數

var x = 1 + 1
x = 3 // 這裡因為用var宣告,所以可以編譯通過    .
println(x * x) // 9

跟值一樣,變數的型別也可以被省略或者被推斷,也可以被顯式的指定出來

var x: Int = 1 + 1

【注:這裡解釋下 scala是一個靜態語言,靜態語言意味著變數一旦型別定下就不可以改變。雖然變數和值都是可以通過編譯器進行型別推斷的,但是型別是不能被修改的。例如:

var a = 10  //變數a 是Int型別,這是編譯器推斷的結果
a = "hello"  //這裡編譯會報錯,因為a的型別已經是Int,再重新賦予字串時就會型別錯誤

程式碼塊

你可以通過 {} 包圍的方式來組合表示式。我們稱之為程式碼塊。

該塊的最後一個表示式的結果也是這個程式碼塊的最終結果:

println({
  val x = 1 + 1
  x + 1
}) // 3

函式

函式是具有引數、並且能傳遞引數值的表示式。

你可以定義一個匿名函式(例如 一個沒有名稱的函式),這個函式的作用是給一個整型的值加1

(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

方法

方法外觀和行為都非常像函式,但是它們之間有一些細微的區別。

方法是通過 def 關鍵字定義, def 後面跟著一個名字,然後緊接著一個引數列表,一個返回結果,和一個程式碼體

def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3

注意方法的返回型別宣告是在引數列表後面用一個 : 接起來的

一個方法可以傳遞多個引數列表:

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
}
println(getSquareString(2.5)) // 6.25

方法的程式碼體內的最後一行表示式的結果也就是方法的返回結果.(Scala有返回關鍵字,但是用的很少) 【注:函式和方法的都是以最後一個變數或表示式作返回結果,你可以認為就是在最後一行程式碼加隱式的加了一個return】

你可以用 class 關鍵字定義一個類,類後面接一個引數列表 就是它的有參構造類

class Greeter(prefix: String, suffix: String) {
  def greet(name: String): Unit =
    println(prefix + name + suffix)
}

greet 方法的返回結果是 Unit ,這表明這個方法沒有任何返回值。 類似於Java和C中的 void。(有一個小區別是,由於Scala的每個表示式都必須返回值,因此Unit實際上也是一個單例的值,只不過這個值是 空(),不包含任何資訊的值)

val greeter = new Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!

我們會在後面的章節對類進行一個深入的講解

Case 類

Scala有一個特殊的類,叫做 case 類。預設的情況下,case類的例項是不可變的,且它們是通過值比較的(區別與普通的類,普通的類是通過引用進行比較的)。這些特性讓case 類在模式匹配中非常的有優勢。

你可以定義case類通過 case class 關鍵字宣告:

case class Point(x: Int, y: Int)

你也可以不用new關鍵字就可以例項化一個case類物件

val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)

case 類是通過值進行比較的,而不是通過引用

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.

關於case 類還有很多需要介紹,我們會在後面的章節繼續深入介紹,相信你會愛上它們的

Objects 伴生物件

Objects 伴生物件是它們自己定義的一個單例例項。你可以把它認為是一個類自身攜帶的一個單例

你可以用 object 關鍵字來定一個伴生物件objects

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

我們將在後面的章節來詳細介紹它

特性 Traits

特性是一個包含某些屬性和方法的抽象資料型別。在Scala繼承中,一個類只能被允許繼承一個其他類,但是它可以被繼承多個特性。

你可以通過 trait 關鍵字來定義特性:

trait Greeter {
  def greet(name: String): Unit
}

特性可以有一個預設的實現:

trait Greeter {
  def greet(name: String): Unit =
    println("Hello, " + name + "!")
}

你可以使用一個 extends 關鍵字來繼承一個特性,並且用 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 只繼承了一個特性,但實際上它可以繼承多個特性。

我們將在後面的章節深入介紹特性

Main 方法

main 方法是Scala程式的入口點。Java虛擬機器也需要一個包含一個String陣列引數的 main 方法。

使用一個 伴生物件object,你可以像如下這樣定義一個main 方法:

object Main {
  def main(args: Array[String]): Unit =
    println("Hello, Scala developer!")
}