自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java下一代: 沒有繼承性的擴(kuò)展,第 1 部分

開發(fā) 后端
Groovy、Scala 和 Clojure 提供了許多擴(kuò)展機(jī)制,但繼承幾乎是 Java™ 語言的惟一選擇。這一期將介紹類別類、ExpandoMetaClass、隱式轉(zhuǎn)換和協(xié)議,借助它們來使用 Java 下一代語言擴(kuò)展 Java 類。

Java 語言的設(shè)計(jì)有目的地進(jìn)行了一定的刪減,以避免前代產(chǎn)品中已發(fā)現(xiàn)的一些問題。例如,Java 語言的設(shè)計(jì)人員感覺 C++ 中的多重繼承性帶來了太多復(fù)雜性,所以它們選擇不包含該特性。事實(shí)上,他們在該語言中很少構(gòu)建擴(kuò)展性選項(xiàng),僅依靠單一繼承和接口。

其他語言(包括 Java 下一代語言)存在巨大的擴(kuò)展?jié)摿?。在本期和接下來的兩期文章中,我將探索擴(kuò)展 Java 類而不涉及繼承性的途徑。在本文中,您會(huì)了解如何向現(xiàn)有類添加方法,無論是直接還是通過語法糖 (syntactic sugar)。

表達(dá)式問題

表達(dá)式問題是最近的計(jì)算機(jī)科學(xué)歷史上的一個(gè)眾所周知的觀察結(jié)果,首創(chuàng)于貝爾實(shí)驗(yàn)室的 Philip Wadler 的一篇未發(fā)表的論文(參見 參考資料)。(Stuart Sierra 在其 developerWorks 文章 “通過 Clojure 1.2 解決表達(dá)式問題” 中出色地解釋了它。在這篇文章中,Wadler 說道:

表達(dá)式問題是老問題的新名字。我們的目標(biāo)是通過案例定義數(shù)據(jù)類型,在這里,在不重新編譯現(xiàn)有代碼的情況下,您可以將新的案例添加到數(shù)據(jù)類型和數(shù)據(jù)類型的新函數(shù)中,同時(shí)保留靜態(tài)類型安全(例如,沒有轉(zhuǎn)換)。

換句話說,您如何向一個(gè)分層結(jié)構(gòu)中的類添加功能,而不求助于類型轉(zhuǎn)換或 if 語句?

我們將通過一個(gè)簡單的例子來表明表達(dá)式問題在真實(shí)世界中的表現(xiàn)形式。假設(shè)您公司始終假設(shè)應(yīng)用程序中的長度單位為,沒有在您的類中為任何其他長度單位構(gòu)建任何功能。但是,有一天,您公司與一家競爭對手合并了,而這個(gè)競爭對手始終假設(shè)長度單位為英尺。

解決該問題的一種方法是,通過使用轉(zhuǎn)換方法擴(kuò)展 Integer,使兩種格式之間的切換變得無關(guān)緊要?,F(xiàn)代語言提供了多種解決方案來實(shí)現(xiàn)此目的;在本期中,我將重點(diǎn)介紹其中的 3 種:

  • 開放類
  • 包裝器類
  • 協(xié)議

Groovy 的類別和 ExpandoMetaClass

Groovy 包含兩種使用開放類 擴(kuò)展現(xiàn)有的類的不同方式,“重新開放” 一個(gè)類定義來實(shí)現(xiàn)更改(例如添加、更改或刪除方法)的能力。

類別類

類別類(一種借鑒自 Objective-C 的概念)是包含靜態(tài)方法的常規(guī)類。每個(gè)方法至少接受一個(gè)參數(shù),該參數(shù)表示方法擴(kuò)充的類型。如果希望向 Integer 添加方法,例如我需要接受該類型作為第一個(gè)參數(shù)的靜態(tài)方法,如清單 1 所示:

清單 1. Groovy 的類別類

  1. class IntegerConv { 
  2.   static Double getAsMeters(Integer self) { 
  3.     self * 0.30480 
  4.   } 
  5.  
  6.   static Double getAsFeet(Integer self) { 
  7.     self * 3.2808 
  8.   } 

清單 1 中的 IntegerConv 類包含兩個(gè)擴(kuò)充方法,每個(gè)擴(kuò)充方法都接受一個(gè)名為 self(一個(gè)通用的慣用名稱)的 Integer 參數(shù)。要使用這些方法,我必須將引用代碼包裝在一個(gè) use 代碼塊中,如清單 2 所示:

清單 2. 使用類別類

  1. @Test void test_conversion_with_category() { 
  2.   use(IntegerConv) { 
  3.     assertEquals(1 * 3.28081.asFeet, 0.1
  4.     assertEquals(1 * 0.304801.asMeters, 0.1
  5.   } 

清單 2 中有兩個(gè)特別有趣的地方。首先,盡管 清單 1 中的擴(kuò)展方法名為 getAsMeters(),但我將它稱為 1.asMeters。Groovy 圍繞 Java 中的屬性的語法糖使我能夠執(zhí)行 getAsMeters() 方法,好像它是名為 asMeters 的類的一個(gè)字段一樣。如果我在擴(kuò)展方法中省略了 as,對擴(kuò)展方法的調(diào)用需要使用空括號,就像 1.asMeters() 中一樣。一般而言,我喜歡更干凈的屬性語法,這是編寫特定于域的語言 (DSL) 的一種常見技巧。

清單 2 中第二個(gè)需要注意的地方是對 asFeetasMeters 的調(diào)用。在 use 代碼塊中,我同等地調(diào)用新方法和內(nèi)置方法。該擴(kuò)展在 use 代碼塊的詞法范圍內(nèi)是透明的,這很好,因?yàn)樗拗屏藬U(kuò)充(有時(shí)是一些核心)類的范圍。

ExpandoMetaClass

類別是 Groovy 添加的第一種擴(kuò)展機(jī)制。但事實(shí)證明對構(gòu)建 Grails(基于 Groovy 的 Web 框架)而言,Groovy 的詞法范圍限制太多了。由于不滿類別中的限制,Grails 的創(chuàng)建者之一 Graeme Rocher 向 Groovy 添加了另一種擴(kuò)展機(jī)制:ExpandoMetaClass。

ExpandoMetaClass 是一種懶惰實(shí)例化的擴(kuò)展持有者,它可從任何類 “成長” 而來。清單 3 展示了如何使用 ExpandoMetaClass,為我的 Integer 類實(shí)現(xiàn)我的擴(kuò)展:

清單 3. 使用 ExpandoMetaClass 擴(kuò)展 Integer

  1. class IntegerConvTest{ 
  2.  
  3.   static { 
  4.     Integer.metaClass.getAsM { -> 
  5.       delegate * 0.30480 
  6.     } 
  7.  
  8.     Integer.metaClass.getAsFt { -> 
  9.       delegate * 3.2808 
  10.     } 
  11.   } 
  12.  
  13.   @Test void conversion_with_expando() { 
  14.     assertTrue 1.asM == 0.30480 
  15.     assertTrue 1.asFt == 3.2808 
  16.   } 

在 清單 3 中,我使用 metaClass holder 添加 asMasFt 屬性,采用與 清單 2 相同的命名約定。對 metaclass 的調(diào)用出現(xiàn)在測試類的一個(gè)靜態(tài)初始化器中,因?yàn)槲冶仨毚_保擴(kuò)充操作在遇到擴(kuò)展方法之前發(fā)生。

類別類和 ExpandoMetaClass 都在內(nèi)置方法之前調(diào)用擴(kuò)展類方法。這使您能夠添加、更改或刪除現(xiàn)有方法。清單 4 給出了一個(gè)示例:

清單 4. 取代現(xiàn)有方法的擴(kuò)展類

  1. @Test void expando_order() { 
  2.   try { 
  3.     1.decode() 
  4.   } catch(NullPointerException ex) { 
  5.     println("can't decode with no parameters"
  6.   } 
  7.   Integer.metaClass.decode { -> 
  8.     delegate * Math.PI; 
  9.   } 
  10.   assertEquals(1.decode(), Math.PI, 0.1

清單4 中的第一個(gè) decode() 方法調(diào)用是一個(gè)內(nèi)置的靜態(tài) Groovy 方法,它設(shè)計(jì)用于更改整數(shù)編碼。正常情況下,它會(huì)接受一個(gè)參數(shù);如果調(diào)用時(shí)沒有任何參數(shù),它將拋出 NullPointerException。但是,當(dāng)我使用自己的 decode() 方法擴(kuò)充 Integer 類時(shí),它會(huì)取代原始類。

Scala 的隱式轉(zhuǎn)換

Scala 使用包裝器類 來解決表達(dá)式問題的這個(gè)方面。要向一個(gè)類添加一個(gè)方法,可將它添加到一個(gè)幫助類中,然后提供從原始類到您的幫助器的隱式轉(zhuǎn)換。在執(zhí)行轉(zhuǎn)換之后,您就可以從幫助器隱式地調(diào)用該方法,而不是從原始類調(diào)用它。清單 5 中的示例使用了這種技術(shù):

#p#

清單 5. Scala 的隱式轉(zhuǎn)換

  1. class UnitWrapper(i: Int) { 
  2.   def asFt = { 
  3.     i * 3.2808 
  4.   } 
  5.  
  6.   def asM = { 
  7.     i * 0.30480 
  8.   } 
  9.  
  10. implicit def unitWrapper(i:Int) = new UnitWrapper(i) 
  11.  
  12. println("1 foot = " + 1.asM + " meters"); 
  13. println("1 meter = " + 1.asFt + "foot"

在清單5中,我定義了一個(gè)名為 UnitWrapper 的幫助器類,它接受一個(gè)構(gòu)造函數(shù)參數(shù)和兩個(gè)方法:asFtasM。在擁有轉(zhuǎn)換值的幫助類后,我創(chuàng)建了一個(gè) implicit def,實(shí)例化一個(gè)新的 UnitWrapper。要調(diào)用該方法,可以像調(diào)用原始類的一個(gè)方法那樣調(diào)用它,比如 1.asM。當(dāng) Scala 未在 Integer 類上找到 asM 方法時(shí),它會(huì)檢查是否存在隱式轉(zhuǎn)換,從而允許將調(diào)用類轉(zhuǎn)換為一個(gè)包含目標(biāo)方法的類。像 Groovy 一樣,Scala 擁有語法糖,因此我能夠省略方法調(diào)用的括號,但這是一種語言特性而不是命名約定。

Scala 中的轉(zhuǎn)換幫助器通常是 object 而不是類,但我使用了一個(gè)類,因?yàn)槲蚁M麄鬟f一個(gè)值作為構(gòu)造函數(shù)參數(shù)(object 不允許這么做)。

Scala 中的隱式轉(zhuǎn)換是一種擴(kuò)充現(xiàn)有類的精妙且類型安全的方式,但不能向開放類一樣,使用這種機(jī)制更改或刪除現(xiàn)有方法。

Clojure 的協(xié)議

Clojure 采用了另一種方法來解決表達(dá)式問題的這個(gè)方面,那就是結(jié)合使用 extend 函數(shù)和 Clojure 協(xié)議 抽象。協(xié)議在概念上類似于一個(gè) Java 接口:一個(gè)沒有實(shí)現(xiàn)的方法簽名集合。盡管 Clojure 實(shí)質(zhì)上不是面向?qū)ο蟮模瞧蛴诤瘮?shù),但您可以與類進(jìn)行交互(并擴(kuò)展它們),并將方法映射到函數(shù)。

為了擴(kuò)展數(shù)字以添加轉(zhuǎn)換,我定義了一個(gè)協(xié)議,它包含我的兩個(gè)函數(shù)(asFasM)。我可使用該協(xié)議 extend 一個(gè)現(xiàn)有類(比如 Number)。extend 函數(shù)接受目標(biāo)類作為第一個(gè)參數(shù),接受該協(xié)議作為第二個(gè)參數(shù),以及一個(gè)使用函數(shù)名為鍵并使用實(shí)現(xiàn)(以匿名函數(shù)形式)為值的映射。清單 6 顯示了 Clojure 單位轉(zhuǎn)換:

清單 6. Clojure 的擴(kuò)展協(xié)議

  1. (defprotocol UnitConversions 
  2.   (asF [this]) 
  3.   (asM [this])) 
  4.  
  5. (extend Number 
  6.   UnitConversions 
  7.   {:asF (fn [this] (* this 3.2808)) 
  8.    :asM #(* % 0.30480)}) 

我可以在 Clojure REPL(interactive read-eval-print loop,交互式讀取-重新運(yùn)算-打印循環(huán))上使用新的擴(kuò)展來驗(yàn)證該轉(zhuǎn)換:

  1. user=> (println "1 foot is " (asM 1" meters"
  2. 1 foot is  0.3048  meters 

在清單6中,兩個(gè)轉(zhuǎn)換函數(shù)的實(shí)現(xiàn)演示了匿名函數(shù)聲明的兩種語法變體。每個(gè)函數(shù)只接受一個(gè)參數(shù)(asF 函數(shù)中的 this)。單參數(shù)函數(shù)很常見,以至于 Clojure 為它們的創(chuàng)建提供了語法糖,如 AsM 函數(shù)中所示,其中 % 是參數(shù)占位符。

協(xié)議創(chuàng)建了一種將方法(以函數(shù)形式)添加到現(xiàn)有類中的簡單解決方案。Clojure 還包含一些有用的宏,使您能夠?qū)⒁唤M擴(kuò)展整合在一起。例如,Compojure Web 框架(參見參考資料)使用協(xié)議擴(kuò)展各種類型,以便它們 “知道” 如何呈現(xiàn)自身。清單 7 顯示了來自 Compojure 中的 Renderable 的一段代碼:

清單 7. 通過協(xié)議擴(kuò)展許多類型

  1. (defprotocol Renderable 
  2.   (render [this request] 
  3.     "Render the object into a form suitable for the given request map.")) 
  4.  
  5. (extend-protocol Renderable 
  6.   nil 
  7.   (render [_ _] nil) 
  8.   String 
  9.   (render [body _] 
  10.     (-> (response body) 
  11.         (content-type "text/html; charset=utf-8"))) 
  12.   APersistentMap 
  13.   (render [resp-map _] 
  14.     (merge (with-meta (response "") (meta resp-map)) 
  15.            resp-map)) 
  16.   IFn 
  17.   (render [func request] 
  18.     (render (func request)  
  19.   ; . . . 

在清單 7中,Renderable 協(xié)議是使用單個(gè) render 函數(shù)來定義的,該函數(shù)接受一個(gè)值和一個(gè)請求映射作為參數(shù)。Clojure 的 extend-protocol 宏(它可用于將協(xié)議定義分組到一起)接受類型和實(shí)現(xiàn)對。在 Clojure 中,您可使用下劃線代替不關(guān)心的參數(shù)。在清單7 中,這個(gè)定義的可看見部分為 nilString、APersistentMapIFn(Clojure 中的函數(shù)的核心接口)提供了呈現(xiàn)指令。(該框架中還包含其他許多類型,但為節(jié)省空間,清單中省略了它們。)可以看到這在實(shí)踐中非常有用:對于您可能需要呈現(xiàn)的所有類型,您可將語義和擴(kuò)展放在一起定義。

結(jié)束語

在本期中,我介紹了表達(dá)式問題,剖析了 Java 下一代語言如何處理以下方面:現(xiàn)有類的干凈擴(kuò)展。每種語言都使用一種不同的技術(shù)(Groovy 使用開放類,Scala 使用包裝器類,而 Clojure 實(shí)現(xiàn)了協(xié)議)來實(shí)現(xiàn)類似的結(jié)果。

但是,表達(dá)式問題比類型擴(kuò)充更深刻。在下一期中,我將繼續(xù)討論使用其他協(xié)議功能、特征和 mix-in 的擴(kuò)展。

原文鏈接:http://www.ibm.com/developerworks/cn/java/j-jn5/index.html

責(zé)任編輯:陳四芳 來源: ibm.com
相關(guān)推薦

2013-05-28 13:44:01

Java下一代

2013-07-27 21:28:44

2013-06-27 11:21:17

2013-05-28 14:02:22

GroovyScala語言

2020-09-27 17:27:58

邊緣計(jì)算云計(jì)算技術(shù)

2018-09-27 18:47:45

AIOpsDevOps

2013-10-11 14:29:44

Java開發(fā)

2013-04-03 09:37:10

JavaJVM

2018-09-11 08:00:00

DevOpsAIOps機(jī)器學(xué)習(xí)

2020-06-02 08:05:28

智能電表蜂窩物聯(lián)網(wǎng)NB-IoT

2024-02-26 14:46:53

移動(dòng)計(jì)算人工智能5G

2025-01-03 09:24:10

模型架構(gòu)論文

2020-09-16 10:28:54

邊緣計(jì)算云計(jì)算數(shù)據(jù)中心

2022-08-08 11:50:45

AIOps網(wǎng)絡(luò)管理

2016-01-26 11:58:12

2009-08-24 17:18:36

下一代網(wǎng)絡(luò)三網(wǎng)融合廣電網(wǎng)絡(luò)

2013-09-09 16:28:36

2010-09-01 17:05:04

無線網(wǎng)絡(luò)

2011-01-27 09:52:43

StuxnetZeus軟件攻擊

2015-01-22 16:16:01

思科IT模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號