【Scala之旅】基礎知識

本節翻譯自html

綜述:介紹了什麼是Scala,Scala的基礎語法、類型層次結構,以及包和引入。java

簡介

歡迎光臨

此旅途包含了一些精簡的介紹:介紹Scala最經常使用的功能。它旨在幫助新手學習這門語言。git

這只是一個簡短的介紹,而不是一個完整的語言教程。若是能夠的話,考慮買本或查閱其餘資源github

什麼是Scala?

Scala是一種現代多範式編程語言,旨在以簡潔,優雅和類型安全的方式表達常見的編程模式。它平滑地集成了面向對象和函數式語言的特性。express

Scala是面嚮對象語言

Scala是一個純面嚮對象語言,在某種意義上來說全部值都是對象。對象的類型和行爲是由特質來描述的。類經過子類化和靈活的基於混入的組合機制擴展爲多重繼承的徹底替代。編程

Scala是函數式語言

Scala仍是一個函數式語言,在某種意義上來說全部函數都是值。Scala提供了輕量級的語法來定義匿名函數,它支持高階函數,容許函數嵌套,並支持柯里化。Scala的樣例類及其對模式匹配模型代數類型的內置支持,用於許多函數式編程語言。單例對象提供了一種便捷的方法來對不是類成員的函數進行分組。數組

此外,Scala的模式匹配概念天然延伸到藉助於忽略右序列模式XML數據處理,經過經過提取器對象進行的通常擴展。在這種狀況下,for推導式有助於制定查詢。這些功能使Scala成爲開發Web服務等應用程序的理想選擇。瀏覽器

Scala是靜態類型語言

Scala配備了一種表達型系統,能夠靜態強制執行以安全和一致的方式使用抽象。具體來講,該類型系統支持:安全

類型推斷意味着用戶不須要使用冗餘類型信息註釋代碼。終上所述,這些特性爲編程抽象的安全重用和軟件的類型安全擴展提供了強大的基礎。dom

Scala是可擴展的

在實踐中,特定領域應用程序的開發一般須要特定領域的語言擴展。Scala提供了獨特的語言機制組合,能夠方便地以庫的形式順利地添加新的語言結構。

在許多狀況下,這能夠在不使用宏等元編程工具的狀況下完成。例如:

  • 隱式類:容許向現有類型添加擴展方法。
  • 字符串插值:用戶可使用自定義插值器進行擴展。

Scala是可互操做的

Scala旨在與流行的Java運行時環境(JRE)良好地互操做。特別是與主流面向對象的Java編程語言的交互儘量平滑。較新的Java功能(如SAM,lambda註解泛型)在Scala中具備直接相似功能。

那些沒有Java相似物的Scala特性,如默認參數帶名參數,儘量接近Java地進行編譯。Scala與Java同樣具備相同的編譯模型(單獨編譯,動態類加載),並容許訪問數千個現有的高質量庫。

基礎

在瀏覽器中嘗試Scala

你能夠在瀏覽器中的ScalaFiddle運行Scala。

  1. 打開 https://scalafiddle.io
  2. 在左窗格中粘貼 println("Hello, world!")
  3. 點擊「運行」按鈕。 輸出顯示在右窗格中。

這是一個簡單的,零設置的方式來測試Scala代碼片斷。

表達式

表達式是可計算的語句。

1 + 1

你可使用 println 輸出表達式的結果。

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

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

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

命名結果,好比這裏的 x,稱爲值。引用一個值並不會從新計算它。

值不能被從新分配。

val x = 1 + 1x = 3 // This does not compile.

編譯器能夠推斷出類型的值,可是您也能夠顯式地聲明類型,以下所述:

val x: Int = 1 + 1

注意,類型聲明 Int 是在標識符 x 以後出現的,而且還須要在二者之間添加一個 :

變量

變量就像值,但你能夠從新分配它們。你可使用 var 關鍵字定義一個變量。

var x = 1 + 1x = 3 // This compiles because "x" is declared with the "var" keyword.
println(x * x) // 9

與值同樣,你能夠顯式地聲明類型:

var x: Int = 1 + 1

代碼塊

你能夠把周圍的表達式結合起來。咱們稱這個爲代碼塊。

塊的最後一個表達式的結果也是整個塊的結果。

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

注意返回類型是在參數列表和冒號 : Int 以後聲明的。

方法可使用多個參數列表。

def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
println(addThenMultiply(1, 2)(3)) // 9

或者根本沒有參數列表。

def name: String = System.getProperty("name")
println("Hello, " + name + "!")

還有一些其餘的區別,可是如今,你能夠把它們看做是相似於函數的東西。

方法也能夠有多行表達式。

def getSquareString(input: Double): String = {
  val square = input * input
  square.toString
}

body 的最後一個表達式是該方法的返回值。(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 的單例值,寫爲 ()。它不攜帶任何信息。)

你可使用關鍵字 new 來建立一個類的實例。

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

以後咱們將詳細介紹類。

樣例類

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.

咱們想要介紹的樣例類還有不少,咱們相信你會愛上它們的!以後咱們將詳細介紹它們。

對象

對象是它們本身定義的單個實例。你能夠把他們想象成他們本身的類的單例。

你能夠經過使用關鍵字 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

稍後咱們將詳細介紹對象。

特質

特徵是包含某些字段和方法的類型。多個特徵能夠組合在一塊兒。

你能夠經過使用關鍵字 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 只擴展了一個單一的特質,可是它能夠擴展多個特質。

稍後咱們將詳細介紹特質。

主方法

主方法是一個程序的入口點。Java虛擬機須要一個命名爲 main 的主方法,並接受一個參數,一個字符串數組。

使用對象,您能夠定義一個主方法,以下:

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

統一類型

在Scala中,全部值都有一個類型,包括數值和函數。下圖演示了類型層次結構的一個子集。

image

Scala類型層次結構

Any 類型是全部類型的父類型,也稱爲頂級類型。它定義了一些通用的方法,如 equalshashCodetoStringAny 有兩個直接子類:AnyValAnyRef

AnyVal 表明值類型。有9種預約義的值類型,它們是不可爲空的:DoubleFloatLongIntShortByteCharUnitBooleanUnit 是一個不具有任何意義的值類型。Unit 只有一個實例能夠像這樣聲明:()。全部函數都必須返回某個值,所以 Unit 是一個有用的返回類型。

AnyRef 表示引用類型。全部的非值類型都被定義爲引用類型。Scala中的每一個用戶定義類型都是 AnyRef 的子類型。若是Scala是在Java運行時環境中使用的,那麼 AnyRef 對應於 Java.lang.object

這裏有一個例子,它演示了字符串、整數、字符、布爾值和函數都是與其餘對象同樣的對象:

val list: List[Any] = List(
  "a string",
  732,  // an integer
  'c',  // a character
  true, // a boolean value
  () => "an anonymous function returning a string"
)

list.foreach(element => println(element))

它定義了類型 List[Any] 的變量列表。這個列表是用各類類型的元素初始化的,可是它們都是 scala.Any 的實例,因此你能夠把它們都添加到列表中。

下面是程序的輸出:

a string
732
c
true
<function>

類型轉換

值類型能夠經過如下方式進行轉換:

image

例如:

val x: Long = 987654321
val y: Float = x  // 9.8765434E8 (note that some precision is lost in this case)

val face: Char = '☺'
val number: Int = face  // 9786

轉換是單向的。下面最後一條語句將沒法經過編譯:

val x: Long = 987654321
val y: Float = x  // 9.8765434E8
val z: Long = y  // Does not conform

你還能夠將引用類型轉換爲子類型。這將在以後的文章中被介紹。

Nothing和Null

Nothing 是全部類型的子類型,也稱爲底部類型。類型 Nothing 是沒有值的。常見的用途是發出非終止信號,例如拋出異常、程序退出或無限循環(即,它是一種沒有對值進行求值的表達式,或者是一種不返回正常值的方法)。

Null 是全部引用類型的子類型(即 AnyRef 的任何子類型)。它有一個由關鍵字 null 標識的單一值。Null 主要用於與其餘JVM語言的互操做性,而且幾乎不該該在Scala代碼中使用。咱們將在稍後的文章中介紹 null 的替代方案。

包和引入

Scala使用包建立命名空間,容許您對程序進行模塊化。

建立package

經過 在Scala 文件的頂部聲明一個或多個包名稱來建立包。

package users

class User

一個約定是將包命名爲與包含 Scala 文件的目錄相同。可是,Scala 不知道文件的佈局。包用戶的 sbt 項目的目錄結構可能以下所示:

- ExampleProject
  - build.sbt
  - project
  - src
    - main
      - scala
        - users
          User.scala
          UserProfile.scala
          UserPreferences.scala
    - test

注意 users 目錄是如何在 scala 目錄中的,以及包中有多個 Scala 文件。包中的每一個 Scala 文件均可以具備相同的包聲明。聲明包的另外一種方法是使用大括號:

package users {
  package administrators {
    class NormalUser
  }
  package normalusers {
    class NormalUser
  }
}

正如您所看到的,這容許封裝嵌套併爲範圍和封裝提供更好的控制。

軟件包名稱應所有小寫,若是代碼是在具備網站的組織內開發的,則應採用如下格式約定:<top-level-domain>.<domain-name>.<project-name>。 例如,若是 Google 有一個名爲 SelfDrivingCar 的項目,則包名稱以下所示:

package com.google.selfdrivingcar.camera

class Lens

這能夠對應於如下目錄結構:

SelfDrivingCar/src/main/scala/com/google/selfdrivingcar/camera/Lens.scala

引入

import 語句用於訪問其餘包中的成員(類,特性,函數等)。訪問同一包的成員不須要 import 語句。import 語句具備選擇性:

import users._  // import everything from the users package
import users.User  // import the class User
import users.{User, UserPreferences}  // Only imports selected members
import users.{UserPreferences => UPrefs}  // import and rename for convenience

Scala與Java不一樣的一個方法是能夠在任何地方使用 import

def sqrtplus1(x: Int) = {
  import scala.math.sqrt
  sqrt(x) + 1.0
}

若是存在命名衝突,而且須要從項目的根目錄導入某些內容,請在 __root__ 前加上包名:

package accounts

import __root__.users._

注意:默認狀況下導入 scalajava.lang 包以及 object Predef

相關文章
相關標籤/搜索