看板 PLT 關於我們 聯絡資訊
所以,我們可以知道,Scala 出來並不是要取代掉 Java,它是要和 Java 共存, 提供程式設計師另一個選擇。其實,只要是 Java 的元件, Scala 都可以拿來使用,但是反過來 Scala 的元件,要寫成 Java 可用, 就可能要少一些東西了,因為 Scala 的元件複雜度還是高於 Java 的。 我們拿 using 來看看 def using[T <: { def close() }] (resource: T) (block: T => Unit) { try { block(resource) } finally { if (resource != null) resource.close() } } def 是定義函式,它的格式是 def 函式名稱(函數傳入值 : 型別) : 傳回值型別 = { 內容 } 但是傳回值型別,其實有時候 編譯器 可以判斷時可以省略的 而後面的 = 在沒有傳回值型別時可以省略,所以就變成 ( 註:函數語言之中,凡函數一定會傳回值,如果沒有傳回值, 則會傳回一個定值 Unit, 雖然它還是有傳回值,但是我們這邊就暫時把它當成無傳回值 ) def 函式名稱(函數傳入值 : 型別) { 內容 } 但是這邊是 def 函式名稱[樣板] (函數傳入值:型別)(函數傳入值:型別) { 內容 } 因為 Scala 支援樣板(template),所以可以使用 Template, 這個樣板等於說是,我們不明確指定要什麼型別,讓 編譯器 於需要時自己生出來 一般寫 [T] 就表示 T 為樣板,但是這邊是用 [T <: {def close() }] <: 是樣板用的繼承,它不是真的像類別的繼承會生出一個新類別, 而是表明樣板必須要是這樣類別的子類別。 為什麼不用 <> 來表示樣板呢? 因為 Scala 中,有內建 XML 型別的 val a = <test>test</test> 這是可以過的, 所以它樣板全部改用 [] 來定義,而原本陣列等使用 [] 的, 則變為和函數用的 () 一樣。 而我們這邊用 {def close() },來表示 Duck Typing, 這原本在動態語言中比較常見,是說只要有這個功能就好了,不管它是怎麼來, 可以避免到繼承樹太過複雜錯亂的情形,而這邊 Scala 也有支援。 眼細的人可能會注意到 (函數傳入值:型別)(函數傳入值:型別) 這樣兩個定義, 它不是寫 (函數傳入值:型別, 函數傳入值:型別) 一般傳入的處理, 因為它處理上不是平行的,也就是說,它在第一個函數傳入值處理完之後, 會先產生出部份的片段,然後再把第二段代入, 像是 using 時,它的兩個函數傳入為: (resource: T)(block: T => Unit) 第一個是資源本身,型別不定,所以設為 T 而第二個是程式區塊,型別是 T => Unit ( 輸入 T ,而輸出 Unit 表示不輸出 ) 它先產生出資源本身,再把資源代入第二段,所以算是分次處理。 所以這樣 using 定義完了之後,接下來使用起來就比原本順手多了。 從此例可見 Scala 也具有強大的 DSL 制定能力, 在 Lift 網頁架構之中,其行數可以和 RoR 相敵,卻又保持型別檢查, 這在語言之中是很少見的。 接下來我們看一下 Scala 少了什麼? Java 有, Scala 沒有的: ●static 靜態 ●primitive types 基礎型別 ●break continue 迴圈控制(但 2.8 版要加回 break) ●interface 介面 ●wildcard ●raw type ●enum 列舉類別 ●static 靜態 -> Scala 使用 object 取代 像 Java class 如下 public class A { private static int count = 0; public static int getCount() { return count; } public A () { count++; } } Scala 的寫法是 : object A { var count = 0 def getCount = count } class A { A.count += 1 } Scala 把 A 拆成兩項,一個 object 一個 class。 ( 註: object 和 class 名稱不一定要相同,但是這邊是從 Java 的例子拆出來的, 所以先使用相同的 )。 注意的是這邊 Scala 預設下是 public, 而 object 中的 count 如果設 private 會讓 class A 也讀不到, 這邊正常要寫 private[A],可是這個 private Scope 比較複雜先暫略, 直接設成 public。 其實,在對物件導向的初學者講解 static 時,常常會有人弄不懂, 不管是說 static 是屬於 class 的,而非屬於 instance 的, 或是說 static 時不用傳入 this,但 instance 時需要傳入 this。 而這邊 Scala 就乾脆拆成兩個定義,類別的放 object,實例的放 class, 所以我們可以看到靜態的有一個 count 初值是 0,這邊型別我們省略會自動放 Int ( 註: Scala 沒有特別區分 int 或 Integer,直接用 Int,但特殊情形之下可以再區別 ) 而前面我們使用 var 表示這是會變的,如果不會變的寫 val ( 類似 Java 的 final )。 而第二行的 def getCount 表示它是一個函式,而它的內容就直接 count, 我們不用寫 def getCount : Int = { return count } 這樣, 可以直接省略成 def getCount = count 其次是 class A 的部份,咦??建構子呢?? 其實 Scala 中,直接包在 class 的部份就算是建構子了, 這邊我們連建構子的宣告定義都省了,就直接把內容包在 class 中, 看起來也清爽多了吧。 而這邊必須寫 A.count += 1,一來是我們必須表明是 object A 中的 count, 它不是自己的 count,另一來 Scala 中沒有 ++, 所以必須使用 += 1 來處理。 ●primitive types 基礎型別 Scala 中強調 Everything is object, 所以一般情形之下,不會使用基礎型別, 它的型別 Byte Short Int Long Char Float Double 都是物件型別。 ●break continue 迴圈控制(但 2.8 版要加回 break) 這是因為函數語言的特性,Scala 決定把這兩個取消, 對於非函數語言式寫法有時需要調整一下, 不過 2.8 把 break 加回來,也許哪一天 continue 也會加回來。 ●interface 介面 -> Scala 使用 trait 取代 這 trait 比 interface 複雜很多,下回分解 ●wildcard Scala 中沒有 List<?> 這樣的,取而代之的是 List<Any> 其中 Any 也是一個物件類別。 ●raw type Scala 中沒有不包含 generic 的 List 它會變成 List[Nothing], 這通常不是我們要的,如果要和 Java 介接時,要以 List[_] 來處理。 另外 Scala 的陣列和 Java 有點不同。 Java: int[] a = new int[10]; Scala: var a : Array[Int] = new Array[Int](10) 型態可省略 var a = new Array[Int](10) ●enum 列舉類別 Scala 使用的是 Enumeration 物件 使用時必須要自訂 class extends Enumeration 由此可見,大部份 "Java 有,而 Scala 沒有" 其實問題都不大, 大多都是 Scala 為了讓程式更加物件化, 或是更偏函數性(去掉 break continue) 等等來做的修改。 一旦這些Scala從Java去掉的部份能夠適應, Java 程式師要轉為 Scala 程式師,並不是很大的障礙, 這障礙其實感覺比 C 轉到 C++ 還來得小。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 163.29.29.137 ※ 編輯: Schelfaniel 來自: 163.29.29.137 (08/17 15:48)