- 标识符由字母、数字、运算符组成
- 一元和二元操作符其实是方法调用
- 操作符优先级取决于第一个字符,结合性取决于最后一个字符
- 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
}