- 標識符由字母、數字、運算符組成
- 一元和二元操作符其實是方法調用
- 操作符優先級取決於第一個字符,結合性取決於最後一個字符
- apply和update方法在對expr(args)表達式求值時被調用
- 提取器從輸入中提取元組或值的序列
- 擴展自Dynamic特質的類型可以在運行期檢視方法名和入參
標識符
- 變量、函數、類等的名稱統稱爲標識符。
- 可以使用Unicode字符
- 還是使用字母數字下劃線那些吧,不用那些亂七八糟的符號了。
- 反引號中可以包含幾乎任何字符序列
scala> val `for`=10
for: Int = 10
中置操作符
a 標識符 b
,其中標識符爲一個帶有兩個參數的方法,一個隱式參數,一個顯式參數- 例如,
1 to 10
是一個方法的調用1.to(10)
,這就是中置表達式,操作符位於兩個參數之間 - 分數的乘法,Fraction類
class Fraction(n:Int,d:Int){
private val num = n
private val den = d
def *(other:Fraction):Fraction={
new Fraction(num*other.num,den*other.den)
}
override def toString: String = s"${num}/${den}"
}
//使用
val a1 = new Fraction(3,4)
val a2 = new Fraction(4,6)
val x = a1 * a2
val y = a1.*(a2)
一元操作符
- 只有一個參數的操作符是一元操作符,中置操作符是二元的,有兩個參數
- 前置操作符:
+ - ! ~
共四個,標識符a
被轉換爲a.unary_操作符
的方法調用。
scala> 1.unary_-
res3: Int = -1
scala> 1.unary_~
res4: Int = -2
- 後置操作符,一元操作符跟在參數後面,
a標識符
轉換爲a.標識符()
的方法調用,使用後置操作符會有警告,可以使用import scala.language.postfixOps
關閉警告。後置操作符可能會引發解析錯誤。
scala> 43.toString()
res7: String = 43
scala> 43 toString
warning: there was one feature warning; re-run with -feature for details
res8: String = 43
賦值操作符
- 形式爲
a 操作符= b
等同於a = a 操作符 b
,例如a += b 等同於a = a + b - 注意:
- <=、>=、!=不是賦值操作符
- 以=開頭的操作符不是賦值操作符(==、 === 、=/=)
- 如果a有一個名爲
操作符=
的方法,那麼該方法會被直接調用
優先級
- 一次使用多個操作符且沒有用括號時,高優先級的操作符先執行
- 除賦值操作符外,優先級由操作符的首字符決定
- 出現在同一行字符產出的操作符優先級相同,例如
+
和->
有相同的優先級 - 後置操作符優先級低於中置操作符
最高優先級:除以下字符外的操作符字符 |
---|
* / % |
+ - |
: |
< > |
! = |
& |
^ |
| |
非操作符的其他符號 |
最低優先級:賦值操作符 |
結合性
- 右結合從右往左算,左結合從左往右算
- 右結合的:
- 以冒號結尾的操作符
- 賦值操作符
- 左結合:除了右結合以爲的操作符
apply和update方法
- f(arg1,arg2,…,argn)等同於f.apply(arg1,arg2,…,argn),在f不是函數或方法的時候
- f(arg1,arg2,…,argn)=value等同於f.update(arg1,arg2,…,argn,value)
- 經常被用於數組和映射
- apply方法經常被用於伴生對象中,構造對象的時候就不用顯示地用new了。apply方法的參數使用構造器的參數。
- 下面的例子中,兩個參數表示分數,三個參數表示帶分數,現在可以直接用Fraction(2,3)和Fraction(2,3,2)
class Fraction (n:Int,d:Int){
private var num = n
private var den = d
def this(x:Int,n:Int, d:Int){
this(n,d)
num = x * d + n
}
def *(other:Fraction):Fraction={
new Fraction(num*other.num,den*other.den)
}
override def toString: String = s"${num}/${den}"
}
object Fraction{
def apply(n: Int, d: Int): Fraction = new Fraction(n, d)
def apply(x:Int,n: Int, d: Int): Fraction = new Fraction(x,n, d)
}
提取器 unapply方法
- 提取器是帶有unapply方法的對象,看做apply方法的逆操作。
- apply接受參數構造對象,unapply方法接受一個對象,然後從中提取值。unapply方法返回的是一個Option對象
- 每一個樣例類case class自動擁有一個apply和unapply方法
class Fraction (n:Int,d:Int){
private var num = n
private var den = d
def this(x:Int,n:Int, d:Int){
this(n,d)
num = x * d + n
}
def *(other:Fraction):Fraction={
new Fraction(num*other.num,den*other.den)
}
override def toString: String = s"${num}/${den}"
}
object Fraction{
def apply(n: Int, d: Int): Fraction = new Fraction(n, d)
def apply(x:Int,n: Int, d: Int): Fraction = new Fraction(x,n, d)
def unapply(arg: Fraction): Option[(Int, Int)] = if(arg.den == 0) None else Some((arg.num,arg.den))
}
//使用
val a1 = new Fraction(3,4)
val a2 = new Fraction(2,4,2)
val Fraction(a,b) = a1*a2
println(s"a = ${a} , b = ${b}")
//a = 24 , b = 8
示例
object Name {
def unapply(arg: String): Option[(String,String)] = {
val pos = arg.indexOf(" ")
if(pos == -1) None else{
Some (arg.substring(0,pos),arg.substring(pos+1))
}
}
}
//
scala> val Name(a,b)="Cay Horstmann"
a: String = Cay
b: String = Horstmann
帶單個參數或無參數的提取器
- 如果unapply方法提取單值,應該返回一個目標類型的Option
object Number{
def unapply(input:String): Option[Int] =
try{
Some(input.trim.toInt)
}catch{
case ex:NumberFormatException => None
}
}
//val是修飾a的
scala> val Number(a)="231"
a: Int = 231
- 提取器也可以只測試輸入,不提取值,unapply方法返回個Boolean類型
unapplySeq方法
- 提取任意長度的字符序列,使用unapplySeq來命名方法
- 不要同時提供相同入參的unapply和unapplySeq方法
object Name {
def unapplySeq(arg:String): Option[Seq[String]] ={
if(arg.trim == "") None else{
Some(arg.trim.split("\\s+"))
}
}
}
//
val author = "Cay Horstmann haha"
author match{
case Name(first,last)=>println(first+"-"+last)
case Name(first,last,m)=>println(first+"-"+last+";"+m)
case _ => None
}