您好,登錄后才能下訂單哦!
本篇文章為大家展示了Groovy與Scala 類怎么在java中使用,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
Groovy 的混入
Groovy 通過 metaClass.mixin() 方法或 @Mixin 注解來實現混入。(@Mixin 注解依次使用 Groovy Abstract Syntax Tree (AST) 轉換,以支持所需的元編程管道。)清單 1 中的示例使用 metaClass.mixin() 讓 File 類能夠創建 ZIP 壓縮文件:
清單 1. 將 zip() 方法混合到 File 類中
class Zipper { def zip(dest) { new ZipOutputStream(new FileOutputStream(dest)) .withStream { ZipOutputStream zos -> eachFileRecurse { f -> if (!f.isDirectory()) { zos.putNextEntry(new ZipEntry(f.getPath())) new FileInputStream(f).withStream { s -> zos << s zos.closeEntry() } } } } } static { File.metaClass.mixin(Zipper) } }
在清單 1 中,我創建了一個 Zipper 類,它包含新的 zip() 方法,以及將該方法添加到現有 File 類的連接。zip() 方法的(不起眼的)Groovy 代碼以遞歸方式創建了一個 ZIP 文件。清單的最后一部分通過使用靜態的初始化程序,將新方法添加到現有的 File 類。在 Java 語言中,類的靜態初始化程序在加載類的時候運行。靜態初始化程序是擴充代碼的理想位置,因為在運行依賴于增強的任何代碼之前,應確保先運行初始化程序。在 清單 1 中,mixin() 方法將 zip() 方法添加到 File。
在 "沒有繼承性的擴展,第 1 部分" 中,我介紹了兩種 Groovy 機制: ExpandoMetaClass 和類別類,您可以使用它們在現有類上添加、更改或刪除方法。使用 mixin() 添加方法的最終結果與使用 ExpandoMetaClass 或類別類添加方法的最終結果相同,但它們的實現是不一樣的。請考慮清單 2 中混入示例:
清單 2. 混入操縱繼承層次結構
import groovy.transform.ToString class DebugInfo { def getWhoAmI() { println "${this.class} <- ${super.class.name} <<-- ${this.getClass().getSuperclass().name}" } } @ToString class Person { def name, age } @ToString class Employee extends Person { def id, role } @ToString class Manager extends Employee { def suiteNo } Person.mixin(DebugInfo) def p = new Person(name:"Pete", age:33) def e = new Employee(name:"Fred", age:25, id:"FRE", role:"Manager") def m = new Manager(name:"Burns", id:"001", suiteNo:"1A") p.whoAmI e.whoAmI m.whoAmI
在清單 2 中,我創建了一個名為 DebugInfo 的類,其中包含一個 getWhoAmI 屬性定義。在該屬性內,我打印出類的一些詳細信息,比如當前類以及 super 和 getClass().getSuperClass() 屬性的父子關系說明。接下來,我創建一個簡單的類層次結構,包括 Person、Employee 和 Manager。
然后我將 DebugInfo 類混合到駐留在層次結構頂部的 Person 類。由于 Person 具有 whoAmI 屬性,所以其子類也具有該屬性。
在輸出中,可以看到(并且可能會感到驚訝),DebugInfo 類將自己插入到繼承層次結構中:
class Person <- DebugInfo <<-- java.lang.Object class Employee <- DebugInfo <<-- Person class Manager <- DebugInfo <<-- Employee
混入方法必須適應 Groovy 中現有的復雜關系,以便進行方法解析。清單 2 中的父類的不同返回值反映了這些關系。方法解析的細節不屬于本文的討論范圍。但請小心處理對混入方法中的 this 和 super 值(及其各種形式)的依賴。
使用類別類或 ExpandoMetaClass 不影響繼承,因為您只是對類進行修改,而不是混入不同的新行為中。這樣做的一個缺點是,無法將這些更改識別為一個不同類別的構件。如果我使用類別類或 ExpandoMetaClass 將相同的三個方法添加到多個類中,那么沒有特定的代碼構件(比如接口或類簽名)可以識別目前存在的共性。混入的優點是,Groovy 將使用混入的一切都視為一個類別。
類別類實現的一個麻煩之處在于嚴格的類結構。您必須完全使用靜態方法,每個方法至少需要接受一個參數,以代表正在進行擴充的類型。元編程是最有用的,它可以消除這樣的樣板代碼。@Mixin 注釋的出現使得創建類別并將它們混合到類中變得更容易。清單 3(摘自 Groovy 文檔)說明了類別和混入之間的協同效應:
清單 3. 結合類別和混入
interface Vehicle { String getName() } @Category(Vehicle) class Flying { def fly() { "I'm the ${name} and I fly!"} } @Category(Vehicle) class Diving { def dive() { "I'm the ${name} and I dive!"} } @Mixin([Diving, Flying]) class JamesBondVehicle implements Vehicle { String getName() { "James Bond's vehicle" } } assert new JamesBondVehicle().fly() == "I'm the James Bond's vehicle and I fly!" assert new JamesBondVehicle().dive() == "I'm the James Bond's vehicle and I dive!"
在清單 3 中,我創建了一個簡單的 Vehicle 接口和兩個類別類(Flying 和 Diving)。@Category 注釋關注樣板代碼的要求。在定義了類別之后,我將它們混合成一個 JamesBondVehicle,以便連接兩個行為。
類別、ExpandoMetaClass 和混入在 Groovy 中的交集是積極的語言進化的必然結果。三種技術明顯有重疊之處,但每種技術都有它們自身才能處理得最好的強項。如果從頭重新設計 Groovy,那么作者可能會將三種技術的多個特性整合在一個機制中。
Scala 的特征
Scala 通過特征 實現了代碼重用,這是類似于混入的一個核心語言特性。Scala 中的特征是有狀態的(它們可以同時包括方法和字段),它們與 Java 語言中的接口扮演相同的 instanceof 角色。特征和混入解決了多個相同的問題,但特征在語言嚴謹性方面獲得了更多的支持。
In "Groovy、Scala 和 Clojure 中的共同點,第 1 部分" 中,我使用了一個復數類來說明 Scala 中的操作符重載。我沒有在該類中實現布爾比較操作符,因為 Scala 內置的 Ordered 特征使得實現變得微不足道。清單 4 顯示了改進的復數類,它利用了 Ordered 特征的優勢:
清單 4. 比較復數
final class Complex(val real:Int, val imaginary:Int) extends Ordered[Complex] { require (real != 0 || imaginary != 0) def +(operand:Complex) = new Complex(real + operand.real, imaginary + operand.imaginary) def +(operand:Int) = new Complex(real + operand, imaginary) def -(operand:Complex) = new Complex(real - operand.real, imaginary - operand.imaginary) def -(operand:Int) = new Complex(real - operand, imaginary) def *(operand:Complex) = new Complex(real * operand.real - imaginary * operand.imaginary, real * operand.imaginary + imaginary * operand.real) override def toString() = real + (if (imaginary < 0) "" else "+") + imaginary + "i" override def equals(that:Any) = that match { case other :Complex => (real == other.real) && (imaginary == other.imaginary) case _ => false } override def hashCode():Int = 41 * ((41 + real) + imaginary) def compare(that:Complex) :Int = { def myMagnitude = Math.sqrt(this.real ^ 2 + this.imaginary ^ 2) def thatMagnitude = Math.sqrt(that.real ^ 2 + that.imaginary ^ 2) (myMagnitude - thatMagnitude).round.toInt } }
我在清單 4 中沒有實現 >、<、<= 和 >= 運算符,但我可以在復數實例中調用它們,如清單 5 所示:
清單 5. 測試比較
class ComplexTest extends FunSuite { test("comparison") { assert(new Complex(1, 2) >= new Complex(3, 4)) assert(new Complex(1, 1) < new Complex(2,2)) assert(new Complex(-10, -10) > new Complex(1, 1)) assert(new Complex(1, 2) >= new Complex(1, 2)) assert(new Complex(1, 2) <= new Complex(1, 2)) } }
因為不需要采用數學上定義的技術來比較復數,所以在 清單 4 中,我使用了一個被人們普遍接受的算法來比較數字的大小。我使用 Ordered[Complex] 特征來 extend 類定義,它混入了參數化的類的布爾運算符。為了讓特征可以正常工作,注入的運算符必須比較兩個復數,這是 compare() 方法的目的。如果您嘗試 extendOrdered 特征,但不提供所需的方法,那么編譯器消息會通知您,因為缺少所需的方法,所以必須將您的類聲明為 abstract。
在 Scala 中,特征有兩個明確定義的作用:豐富接口和執行可堆疊的修改。
豐富接口
在設計接口時,Java 開發人員面臨著一個取決于便利性的難題:應該創建包含很多方法的富 接口,還是創建只有幾個方法的瘦 接口?富接口對于其消費者更方便一些,因為它提供了廣泛的方法調色板,但方法的絕對數量使得接口更加難以實現。瘦接口的問題剛好相反。
特征可以解決使用富接口還是薄接口的這種兩難問題。您可以在瘦接口中創建核心功能,然后使用特征擴充它,以提供更豐富的功能。例如,在 Scala 中,Set 特征實現了一個設置好的共享功能,您選擇的子特征( mutable 或 immutable)已經決定了設置是否可變。
可堆疊的修改
Scala 中的特征的另一個常見用途是可堆疊的修改。利用特征,您可以修改現有的方法并添加新的方法,super 提供了對可以鏈接回以前的特征實現的訪問。
清單 6 通過一些隊列說明了可堆疊的修改:
清單 6. 構建可堆疊的修改
abstract class IntQueue { def get():Int def put(x:Int) } import scala.collection.mutable.ArrayBuffer class BasicIntQueue extends IntQueue { private val buf = new ArrayBuffer[Int] def get() = buf.remove(0) def put(x:Int) { buf += x } } trait Squaring extends IntQueue { abstract override def put(x:Int) { super.put(x * x) } }
在清單 6 中,我創建一個簡單的 IntQueue 類。然后,我構建一個可變的版本,該版本中包括 ArrayBuffer。Squaring 特征擴展了所有 IntQueue,并在值被插入隊列中時自動對其進行平方計算。在 Squaring 特征內對 super 的調用提供對堆棧中前面的特性的訪問。除了第一個方法之外,只要每個被重寫的方法調用 super,修改堆棧就會一個一個地堆疊上去,如清單 7 所示:
清單 7. 構建堆疊的實例
object Test { def main(args:Array[String]) { val queue = (new BasicIntQueue with Squaring) queue.put(10) queue.put(20) println(queue.get()) // 100 println(queue.get()) // 400 } }
super清單 6 中對 super 的使用說明了特征和混入之間的重要區別。因為您在創建原始的類后(確實)混入了它們,所以混入必須解決類層次結構中的當前位置上的潛在不確定性。特征在創建類的時候已被線性化;編譯器解決了什么是 super 的問題,沒有不確定性。嚴格定義的復雜規則(這超出了本文的范圍)控制了線性化在 Scala 中的工作方式。特征還為 Scala 解決了鉆石問題。當 Scala 跟蹤方法的源和解析時,不可能出現不確定性,因為該語言定義了明確的規則來處理解析。
上述內容就是Groovy與Scala 類怎么在java中使用,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。