相比Java
、C++
等語言,Scala
融合了OOP
、FP
等編程范式,同時語法上更靈活。
var
、val
定義變量
、常量
,類型可以由編譯器推導,也可以顯式指定。定義變量時甚至可以省略var
、val
關鍵字,無關鍵字時定義的變量默認即為val
,在定義變量的同時就需要初始化變量,否則報錯(抽象類中除外)。def
關鍵字定義方法,var
、val
定義函數,需要注意的是使用var
定義的函數是可以更改實現的,但def
定義的方法一經定義實現就不可改變。Int
、Double
等都是類,函數/方法返回值的空類型為Unit
,相當於Java/C++中的void
。Enumeration
。if
語句表達式帶有返回值,可以實現類似效果。public
,因而沒有public
關鍵字,但有private
和protected
關鍵字,作用與Java大致相同,但支持更細粒度的權限控制。創建文件Test.scala
,輸入以下代碼:
object Test {
def main(args: Array[String]): Unit //帶有等號的方法可以省略返回值類型由編譯器進行推導
= println("Hello World!")
}
與Java類似,Scala也是從主方法main
中開始執行整個程序,不過main方法並不定義在類中,而是定義在單例對象中(使用object關鍵字創建單例對象)。
將主方法寫在class中能夠通過編譯,但生成的字節碼文件在執行時會出錯。
也可以不手動定義main方法而去讓伴生對象繼承App
特質,即可直接執行代碼語句,例如:
object Test extends App {
println("Hello World!")
}
單例對象的名稱可以與源碼文件的文件名不同。
與Java不同,Scala中同時支持函數
與方法
(Java只有方法而沒有真正意義上的”函數”,只有與”函數”類似的”靜態方法”)。
方法由def
關鍵字定義,可以被def方法、val函數重寫。一個典型的方法格式如下:
def methodName(args: Type):Type = {
/* function_body */
}
Scala中方法體不需要顯式使用return
關鍵字來給出方法返回值,編譯器會將函數體的最后一句代碼推導出類型做為整個函數的返回值。
對於有返回值的方法,必須要在方法定義中加入等號,否則編譯器不會推導返回值。
即使方法的返回值為Unit
,只要顯式指定了返回值類型,則必須在方法體中加入等號。
方法和函數的形參不需要也不能使用val
、var
關鍵字聲明,只需寫明類型即可。
在Scala中,方法允許省略參數,空的參數表可以直接省略,如:
def getNum: Int = 100
def getNum(): Int = 100 //以上兩個定義作用相同,但只能存在一個
無參方法與空參方法只能存在一個,但二者在使用方式上略有不同,無參方法在調用時只能直接使用方法名,在方法名后加上括號調用就會出錯;但空參方法既可以使用帶有括號的方法調用方式,也可以省略括號,例如:
scala> def getNum: Int = 100 //定義了方法 getNum: Int
getNum: Int
scala> getNum //正確,返回 100
res0: Int = 100
scala> getNum() //錯誤,提示 error: Int does not take parameters
<console>:12: error: Int does not take parameters
getNum()
^
scala> def getNum(): Int = 200 //定義了方法 getNum(): Int
getNum: ()Int
scala> getNum //正確,返回 200
res1: Int = 200
scala> getNum() //正確,返回 200
res2: Int = 200
同時,無參方法不能與已有字段名稱相同(編譯報錯),而空參方法允許帶有同名的字段。
需要注意的是,在Scala中,賦值表達式的值為Unit
,而不是類似Java/C++中的以被賦值的變量類型為表達式的值。例如:
scala> var _num = 0
_num: Int = 0
scala> def testNum(num: Int): Int = _num = num //由編譯器推斷出的返回值類型為Unit
<console>:12: error: type mismatch;
found : Unit
required: Int
def testNum(num: Int): Int = _num = num
^
在Scala中,方法中參數允許帶有默認值:
scala> var num = 100
num: Int = 100
scala> def setNum(p: Int = 0) { num = p } //方法的參數不能由默認值進行類型推導,即使給參數寫明了默認值,也依然需要顯式指明類型
setNum: (p: Int)Unit
scala> setNum() //對於有參數的方法,即使參數帶有默認值使得參數表可以為空但在調用時依然不能省略括號,否則報錯
scala> println(num)
0 //輸出0
如果一個方法中包含多個同類型並帶有默認值的參數,調用時默認匹配第一個參數:
scala> def func(num1: Int = 100, num2: Int = 200) = println(s"$num1 $num2")
func: (num1: Int, num2: Int)Unit
scala> func(300)
300 200
在Scala中,調用方法時可以在參數表中寫明參數的名稱,該特性被稱為”具名參數”。
對於方法中包含多個同類型並帶有默認值參數的情況下,使用該特性可以顯式指定要傳入的是哪一個參數:
scala> func(num2 = 300)
100 300
與C++不同,Scala中,方法參數的默認值不需要連續,參數的默認值可以交錯出現,甚至是顛倒參數順序:
scala> def func(int: Int, str: String = "String", char: Char, double: Double = 123.0) = println(s"$int $str $char $double")
func: (int: Int, str: String, char: Char, double: Double)Unit
scala> func(100, 'c')
<console>:12: error: not enough arguments for method func: (int: Int, str: String, char: Char, double: Double)Unit.
Unspecified value parameter char.
func(100, 'c')
^
scala> func(int = 100, char = 'c') //對於默認參數不連續的方法,需要使用"具名參數"
100 String c 123.0
在Scala中,若一個帶有默認的參數的方法省略默認參數時簽名與一個已經存在的方法相同,編譯器並不會報錯(C++編譯器則會報錯),而是在調用方法時優先使用無默認值的版本(處理邏輯類似於C#):
object Main extends App {
def func() = println("No Args")
def func(num: Int = 100) = println(num)
func()
}
輸出結果:
No Args
在Scala中函數使用var``val
關鍵字定義,即函數是一個存儲了函數對象的字段。
一個典型的函數定義如下:
var functionName: FuncType = 符合簽名的方法/函數/Lambda
Scala中的函數類型為Function
,根據參數數目的不同,Scala中提供了Function0[+R]
(無參數)到Function22[-T1, ..., -T22, +R]
共23種函數類型,即Scala中的函數,最多可以擁有22個參數。
與方法不同,函數不能夠帶有默認值。
需要注意的是,函數不允許省略參數,因為函數名做為表達式時的語義為函數名所代表的函數內容而非函數調用。空參函數的括號不可省略,直接使用函數名並不代表調用空參函數,比如:
scala> var show100: () => Int = () => 100
show100: () => Int = <function0>
scala> show100 //直接使用函數名得到的是函數對象而非調用函數
res0: () => Int = <function0>
scala> show100()
res1: Int = 100
在Scala中,可以直接使用Lambda創建匿名函數后立即使用:
scala> ((str: String) => println(str))("Hello World!")
Hello World!
與C++中的Lambda用法類似:
#include <iostream>
using namespace std;
int main(void)
{
[](const string& str) { cout << str << endl; } ("Hello World!");
return 0;
}
然而在C#中,Lambda需要創建對象或顯式指明類型才能使用,同樣的語句需要寫成:
using System;
class Test
{
static void Main(string[] args)
=> new Action<string>(str => Console.WriteLine(str))("Hello World!");
//或者寫成 ((Action<string>)(str => Console.WriteLine(str)))("Hello World!");
}
在Scala中,函數允許進行組合。
函數組合有兩種方式,a compose b
實際調用次序為a(b())
,a andThen b
實際調用次序為b(a())
。
需要注意的是,方法不能直接進行組合,需要將其轉化為函數(方法名之后加_
符號)。
object Main extends App {
def add(num: Int) = num + 100
def double(num: Int) = num * 2
//只有函數能進行組合,方法需要加"_"符號轉化成函數
var compose = add _ compose double
var andThen = add _ andThen double
println(compose(100) == add(double(100)))
println(andThen(100) == double(add(100)))
}
輸出結果:
true
true
當一個方法接收的參數為空時,該參數即為傳名參數(By-name Parameter),如下所示:
def func(arg: => T) ...
可以使用傳名參數可以接收任意數量的代碼,如下所示:
object Main extends App {
def show(args: => Unit) = args
//單行語句可直接作為參數
show(println("123"))
//多行語句可放在大括號中
show {
println("456")
println("789")
}
}
運行結果:
123
456
789
Scala為函數式編程語言,在Scala中函數對象可以直接作為參數傳遞。
當函數作為參數存在時,傳名參數與普通的空參函數參數定義不能同時存在,如下定義只能存在一個:
def func(arg: () => T) = arg
def func(arg: => T) = arg
var func: (() => T) => T = (arg: () => T) => arg
在接收參數時,空參函數參數只能接收同樣空參的函數,即() =>
不能被省略,而傳名參數則無此限制。
在Scala中,所有的類型皆為對象,所有類型都從根類Any
繼承,Any
有AnyVal
和AnyRef
兩個子類。
在Scala中,基礎類型如Int
、Float
、Double
、Unit
等全部從AnyVal
類中派生,因而可以直接在泛型中直接使用這些類作為類型參數。
同時,Scala中提供了隱式轉換(ImplicitConversion)
來保證Int``Float``Double
等類型之間可以自動進行轉換。
基礎類型與字符串(String)等類型之間的轉換也由類提供的成員函數進行,如將數值與字符串相互轉換可以使用如下代碼:
var str = 100.toString
var num = str.toInt
在Scala中,所有的基礎類型之外的引用類型派生自類AnyRef
。
與Java不同,Scala中存在底類型(bottom)。底類型包括Nothing
和Null
。
Nothing
是所有類型Any
的子類型,定義為final trait Nothing extends Any
。Nothing
特質沒有實例。Null
是所有引用類型AnyRef
的子類型,定義為final trait Null extends AnyRef
。Null
特質擁有唯一實例null
(類似於Java中null
的作用)。在Scala中,使用Option[T]
表示可空類型,Option[T]
包含兩個子類,Some[T]
和None
,分別代表值存在/值不存在。
對Option[T]
類型使用getOrElse()
方法來獲取存在的值或是當值不存在時使用指定的值,如下所示:
scala> var str1: Option[String] = "test"
<console>:10: error: type mismatch; //賦值失敗,Option[T]只能接收Option及其子類
found : String("test")
required: Option[String]
var str1: Option[String] = "test"
^
scala> var str1: Option[String] = Option("test")
str1: Option[String] = Some(test)
scala> var str2: Option[String] = None
str2: Option[String] = None
scala> println(str1 getOrElse "Get Value Failed!")
test
scala> println(str2 getOrElse "Get Value Failed!")
Get Value Failed! //輸出getOrElse()方法中設定的值
可空類型也可以用於模式匹配中,如下代碼所示:
var str = 100.toString
var num = str.toInt
在Scala中,所有的基礎類型之外的引用類型派生自類AnyRef
。
與Java不同,Scala中存在底類型(bottom)。底類型包括Nothing
和Null
。
Nothing
是所有類型Any
的子類型,定義為final trait Nothing extends Any
。Nothing
特質沒有實例。Null
是所有引用類型AnyRef
的子類型,定義為final trait Null extends AnyRef
。Null
特質擁有唯一實例null
(類似於Java中null
的作用)。在Scala中,使用Option[T]
表示可空類型,Option[T]
包含兩個子類,Some[T]
和None
,分別代表值存在/值不存在。
對Option[T]
類型使用getOrElse()
方法來獲取存在的值或是當值不存在時使用指定的值,如下所示:
scala> var str1: Option[String] = "test"
<console>:10: error: type mismatch; //賦值失敗,Option[T]只能接收Option及其子類
found : String("test")
required: Option[String]
var str1: Option[String] = "test"
^
scala> var str1: Option[String] = Option("test")
str1: Option[String] = Some(test)
scala> var str2: Option[String] = None
str2: Option[String] = None
scala> println(str1 getOrElse "Get Value Failed!")
test
scala> println(str2 getOrElse "Get Value Failed!")
Get Value Failed! //輸出getOrElse()方法中設定的值
可空類型也可以用於模式匹配中,如下代碼所示:
object TestOption extends App {
var s = List(Some(123), None)
for (num <- s)
num match {
case Some(x) => println(x)
case None => println("No Value")
}
}
運行結果:
123
No Value
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。