程式扎記: [Scala 小學堂] Scala Gossic : 了解更多 - 定義類別 (定義操作方法)

標籤

2016年6月14日 星期二

[Scala 小學堂] Scala Gossic : 了解更多 - 定義類別 (定義操作方法)

轉載自 這裡
前言 :
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. Scala 本身具有擴充性,不過這必須了解更多語法特性與細節. Scala 建構式有些限制,使用方法可以定義所謂運算子(Operator),提供語法蜜糖與存取方法.

定義操作方法 :
在 Scala 中,如果方法只接受一個引數,則可以省略. 與括號. 例如 :
- ClassMeth1.scala 代碼 :
  1. class Account(val id: String, val name: String) {  
  2.         private var bal: Int = 0  
  3.   
  4.         def deposit(amount: Int) {  
  5.                 require(amount >0)  
  6.                 bal += amount  
  7.         }  
  8.   
  9.         def withdraw(amount: Int) {  
  10.                 require(amount > 0 && amount < bal)  
  11.                 bal -= amount  
  12.         }  
  13.   
  14.         def balance = bal  
  15. }  
  16.   
  17. val acct = new Account("123""John")  
  18. acct deposit 100  // 等於 acct.deposit(100)  
  19. acct withdraw 50  // 等於 acct.withdraw(50)  
  20. println(acct.balance)  // 顯示 50  

這讓方法在操作時像是個內建的運算子。例如,你可以定義一個複數類別如下操作 :
- ClassMeth2.scala 代碼 :
  1. class Complex(val re: Double, val im: Double) {  
  2.     def add (that: Complex) = new Complex(re + that.re, im + that.im)  
  3.     def minus (that: Complex) = new Complex(re - that.re, im - that.im)  
  4.     override def toString =  
  5.       re + (if (im < 0" - " + (-im) else " + " + im) + " i"  
  6.   
  7. }  
  8.   
  9. val c1 = new Complex(42)  
  10. val c2 = new Complex(21)  
  11. println(c1 add c2)       // 顯示 6.0 + 3.0 i  
  12. println(c1 minus c2)     // 顯示 2.0 + 1.0 i  

上 例中,toString 是由 scala.AnyRef 繼承下來,你將整重新定義(Override)傳回字串描述,像 println 函式會直接使用 toString 的傳回值來顯示,在 Scala 中,要重新定義父類別中的某個方法,一定得使用 override 關鍵字,這之後還會詳談. 事實上,在Scala中,像+、-、*、/ 這類的「運算子」,其實都是方法名稱(Scala 實際上來說並沒有運算子)。你可以直接如下定義複數類別與簡單的運算 :
- ClassMeth3.scala 代碼 :
  1. class Complex(val re: Double, val im: Double) {  
  2.     def + (that: Complex) = new Complex(re + that.re, im + that.im)  
  3.     def - (that: Complex) = new Complex(re - that.re, im - that.im)  
  4.     def * (that: Complex) = new Complex(re * that.re - im * that.im,  
  5.                                         re * that.im + im * that.re)  
  6.     def / (that: Complex) = {  
  7.       val d = that.re * that.re + that.im * that.im  
  8.       new Complex((re * that.re + im * that.im) / d,  
  9.                   (im * that.re - re * that.im) / d)  
  10.     }  
  11.     override def toString =  
  12.       re + (if (im < 0" - " + (-im) else " + " + im) + " i"  
  13.   
  14. }  
  15.   
  16. val c1 = new Complex(42)  
  17. val c2 = new Complex(21)  
  18.   
  19. println(c1 + c2)    // 顯示 6.0 + 3.0 i  
  20. println(c1 - c2)    // 顯示 2.0 + 1.0 i  
  21. println(c1 * c2)    // 顯示 6.0 + 8.0 i  
  22. println(c1 / c2)    // 顯示 2.0 + 0.0 i  

在傳統語言中的運算子,在Scala中可以定義為方法的有 :
* 算術操作 : 如+、-、*、/、%
* 關係操作 : 如>、>=、<、<=
* 邏輯操作 : 如&&、||、!
* 位元操作 : 如&、|、~、^、>>、>>>、<<
* 指定操作 : 如+=、-=、*=、/=、%=、&=、|=、~=、>>=、>>>=、<<=

注意!你不可以定義 == 與 !=,因為 == 與 != 定義在 scala.Any 中,且被宣告為 final,你也不可以重新定義 eq 與 ne,因為它們定義在 scala.AnyRef 中,也是被宣告為final,如果你要定義物件內容值是否相同,則要重新定義 equals() 方法(與 Java 相同),因為 scala.Any 中定義的 == 或 != 方法,會呼叫equals() 來檢查物件的相等性[/color]. 對於一些運算如補數 ~ 這樣的操作,是擺在某個數值或物件之前的運算,Scala 使用「unary_」作為這種前置運算的方法名稱前置,例如若要定義 ~ 這樣的運算,則在定義類別時定義「unary_~」這樣的方法名稱,當你呼叫 ~2,實際上等於呼叫 (2).unary_~ :
scala> ~2
res0: Int = -3

scala> (2).unary_~
res1: Int = -3

不過,Scala 限制可以作為前置運算的符號,只有 +、-、! 與 ~ 可以作為前置運算,如果你定義其它的符號,例如定義了 unary_#,你仍然可以使用 x.unary_# 的方式呼叫(x 參考至物件),但 #x 這樣的前置運算則是不行的(Scala 的將之解釋為#.x

補充說明 :
Wiki : 複數 (數學)
通過形式上應用代數的結合律、交換律和分配律,再加上等式i ² = −1,定義複數的加法、減法、乘法和除法:
加法: 
減法: 
乘法: 
除法: 


沒有留言:

張貼留言

網誌存檔