操作符

操作符會涉及到以下部分的內容:

1.實現自己的操作符;

2.隱式轉換(自動被應用的類型轉換);

3.apply、udpate和unapplay這些特殊方法。


1.中置操作符:

Scala中包含下邊的表達式:

a 標識符 b

其中的標識符代表一個帶有兩個參數的方法(一個隱式的參數和一個顯示的參數)。

例如:1 to 10 實際上是 1.to(10)。

這樣的表達式叫做中置表達式,因爲操作符位於兩個參數之間。如果在自己的類中定義操作符,需要以你想要用作操作符的名稱來定義一個方法。

下面定義的這個類,自定義了一個對象的操作符:

/**
 * 定義兩個對象相乘的操作符,來實現如下公式:(n1/d1) * (n2/d2) = (n1*n1) / (d1*d2)
 * Infixtest包含2個成員num和dev, 定義乘號(*) 即:Infixtest的2個對象(infix1和infix2)相乘 , 
 * 也就是: Infix1 * Infix2 = (infix1.n1 * infix2.n2)/(infix1.d1 * infix2.d2)
 */
class Infixtest(val num:Double , val dev:Double){
  def *(other:Infixtest) = {
    (num * other.num)/(dev*other.dev)
  }
}

測試語句:

def main(args:Array[String]) {
  
val infix1 =  new Infixtest(2.1,2)
val infix2 =  new Infixtest(10.2,4)
println("兩個對象的乘積:" + infix1.*(infix2))
    
}

這樣我們就自定義了*(當然可以選擇任何你喜歡的操作符,如#*等)實現了對象的相乘。


2.一元操作符:

只有一個參數的操作符稱爲一元操作符。如果操作符出現在參數後,那麼就是一個後置操作符(即: a 標識符 )。相反則是前置操作符(即:標識符 a)。

後置操作符:

1 toString 等同於 1.toString()

前置操作符:

+ - ! ~ 可以作爲前置操作符。它們被轉換成對名爲unary_操作符的方法調用。

-a  等同於  a.unary_-

println(1.unary_-)


3.優先級:

在有多個操作符同時出現,但是又沒有括號時,判斷操作符的優先級就十分重要了。

Scala可以隨意定義操作符,因此需要用一套優先級判定方案,對所有操作符生效,但同時保留人們所熟悉的標準操作符的優先順序。

除了賦值操作符外(a += 2 ),優先級由操作符的首字符決定。

後置操作符優先級低於中置操作符:

a 中置操作符 b 後置操作符  等同於:  (a 中置操作符 b) 後置操作符


當有一系列同優先級的操作符時,操作符的結合性決定了它們是從左到右還是從右到左求值。絕大部分操作符都是左結合的(例如,12+8+21 等同於 (12+8)+21),除了以下兩種情況:

  1. 以冒號(:)結尾的操作符;

  2. 賦值操作符


4.apply和update方法:

函數或者方法可以如下邊方式調用:

f(arg1,arg2,...)

如果f不會函數或者方法,那麼這個表達式就等同於調用apply方法:

f.apply(arg1,arg2,...)

但是如果它出現在賦值語句的等號的左側,即:f(arg1,arg2,...) = value,則等同於調用update方法:

f.update(arg1,arg2,...)

下面是一個使用apply和update方法的實例:

/**
 * apply方法和update方法:
*/
import scala.collection.mutable.HashMap
val hashtest = new HashMap[String,Int]
//f(arg1,arg2,...)在賦值語句等號的左側,相當於調用 f.update。 
//下邊語句相當於  hashtest.update("Bob",131) 
hashtest("Bob") = 131
//相當於hashtest.apply("Bob")
val bobhash = hashtest("Bob")

apply方法也經常被用在伴生對象,用來構造對象而不用顯式地使用new。


5.提取器:

所謂提取器就是一個帶有unapply方法的對象。unapply方法是伴生對象中apply方法的反向操作。

apply方法接收構造參數,然後將其變爲對象。而unapply方法接收一個對象,然後從中提取值——通常這些值就是當初用來構造該對象的值。

通常而言,模式匹配可能會失敗。因此unapply方法返回的是一個option。它包含一個元組,每個匹配到的變量各有一個值與之對應。

下面使用上邊的Infixtest類的例子,首先定義伴生對象(需要定義unapply方法):

/**
 * 定義兩個對象相乘的操作符,來實現如下公式:(n1/d1) * (n2/d2) = (n1*n1) / (d1*d2)
 * Infixtest包含2個成員num和dev, 定義乘號(*) 即:Infixtest的2個對象(infix1和infix2)相乘 , 
 * 也就是: Infix1 * Infix2 = (infix1.n1 * infix2.n2)/(infix1.d1 * infix2.d2)
 */
class Infixtest(val num:Double , val dev:Double){
  def *(other:Infixtest):Infixtest = {
    Infixtest(num * other.num,dev*other.dev)
  }
}

//apply伴生對象
object Infixtest{
  //apply方法接收2個參數來創建對象。 可以省去原來根據類創建對象使用new關鍵字。
  def apply(num:Double,dev:Double) = new Infixtest(num,dev)
  //unapply方法接收一個對象,獲得後從中提取值————通常這些值就是當初用來構造該對象的值
  def unapply(other:Infixtest) = {
    if(other.dev == 0)
        None
    else
       Some((other.num,other.dev))
  }
}

提取器的定義:

var Infixtest(a:Double,b:Double) = Infixtest(134.2,3) * Infixtest(2,12)
println("Unapply方法提取器:"+ a + " " + b)

下面,使用提取器來獲取一個姓名的姓和名字符串:

/**
 * 實現unapply提取器,使用提取器來實現簡單功能,給一個姓名,分別提取出姓和名
 * 定義unapply方法
 */
object Name{
  def unapply(other:String) ={
    val names = other.split(" ")
    if (names.length != 2 ) None
    else Some( (names(0),names(1)) )
  }
}

測試類:

  /**
   * 測試分拆姓名的unapply方法
  */
  val test_name = "Tom White"
  var Name(c,d) = test_name
  println("名是:" + c +" 姓是:" +d)

上邊例子,沒有定義Name類。Name對象是針對String對象的提取器

另外,每一個樣例類都自動具備apply和unapply方法。例如:

case class Currency(value:Double,unit:String)


6.帶單個參數或無參數的提取器:

如果unapply方法要提取單值,則它應該返回一個目標類型的Option。提取器也可以只是測試其輸入而並不真的將值提取出來。

/**
 * 提取器提取一個或者不返回值
 */
//將數字轉化爲Int
object Number{
  def unapply(num:String) = {
    Some(Integer.parseInt(num))
  }
}
//確定字符串是否含空格
object IsCompond{
   def unapply(num:String) = {
    Some(num.contains(" "))
  }
}

測試語句:

  /**
   * 測試 :提取器提取一個或者不返回值
   */
  var Number(sf) = "332"
  var IsCompond(s) = "new York"
  println("數字解析:" + sf + ";Boolean解析:" + s)


7.unapplySeq方法:

要提取任意長度的序列,應該使用unapplySeq來命名方法。返回一個Option[Seq[A]],A是被提取的值的類型。

/**
 * 提取器提取一系列名字。
 */
object Names{
  def unapplySeq(args:String):Option[Seq[String]] = {
    //一個或多個空格分隔
    if (args.split("\\s+").length > 1) { Some(args.split("\\s+"))}
    else None
    }
  }

測試:

  /**
   * 測試unapplySeq方法
   */
  var Names(a1,a2,a3,a5,_,_) = "Let us go to park today!"
  println (a1+"||"+a2+"||"+a3+"||"+a5+"||")

需要注意的是,後邊拆分出來的元組,必須跟前邊Names()裏的變量個數一致,如果不需要的話用佔位符號。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章