Java與groovy混編 —— 一種兼顧接口清晰和實現敏捷的開發方式



  1. 有大量平均水平左右的“工人”可被選擇、參與進來 —— 這意味着好招人
  2. 有成熟的、大量的程序庫可供選擇 —— 這意味着大多數項目都是既有程序庫的拼裝,標準化程度高而定製化場景少
  3. 開發工具、測試工具、問題排查工具完善,成熟 —— 基本上沒有團隊願意在時間緊、任務重的項目情況下去做沒有把握的、基礎開發工具類的技術試探
  4. 有面向對象特性, 適合大型項目開發 —— 無數大型項目已向世人述說,“面向對象”是開發大型軟件的優秀代碼組織結構
  5. 能適應大型團隊、多人協作開發 —— 代碼需要簡單易懂,起碼在接口、api層面是這樣

—— 這是我所理解的“工業化開發編程語言”的概念

很顯然, java就是種典型的“工業語言”, 非常流行,很多企業靠它賺錢,很實際;
但java也是常年被人黑,光是對其開發效率的詬病就已經足夠多,不過java始終屹立不倒;

這樣的局面其實無所謂高興還是擔憂,理性的程序員有很多種,其中一種是向“錢”看的 —— 我寫java代碼,就是因爲工作需要而已,能幫助我的組織搞定業務,做出項目,這很好;
當有人說java語言不好的時候,理性的程序員不會陷入宗教式的語言戰爭之中,他會思考這些人說的是否有道理;如果真的發現整個java平臺大勢已去,他會毫不猶豫地扭頭就走,不過直到目前爲止,還沒有這種跡象出現;

那麼,從這些無數次的口水之爭中,我們能否從別人的“戰場”上發現一些有用的東西, 來改進我們的開發方式,從而使得java這種已經成爲一個“平臺”的東西走得更遠,賺更多的錢呢?
答案是“有的”,感謝那些參與口水戰爭的、各種陣營的年輕程序員們,有了你們,java speaker們纔有了更多的思考;

我就只談一個最實際的問題:

java被吐槽的這些年, 就開發效率這一點而言,到底有哪些東西是值得借鑑的?

也就是說,到底是哪些主要特性直接導致了某些其它語言在語法上相對於java的優越感?

豐富的literal定義

在groovy中定義map和list的慣用方式:

def list = [a, 2 ,3]
def map = [a:0, b:1]

而java呢?只能先new一個list或map,再一個個add或put進去; 上面這種literal(字面量)形式的寫法便捷得多;

而javascript在這方面做得更絕, 我們都用過json,而json其實就是literal形式的object

極端情況下,一門編程語言裏的所有數據類型,包括”內建”的和用戶自定義的,統統可以寫成literal形式;
在這種情形下,其實這種語言連額外的對象序列化、反序列化機制都不需要了 —— 數據的序列化形式就是代碼本身, “代碼”和“數據”在形式上被統一了

java對這方面幾乎沒有任何支持,對於提高編碼效率來講,這是值得學習的一點, 起碼“內建”數據結構需要literal寫法支持

first-class function & higher-order function & function literal(lambda)

無論是js, 還是python/ruby,或是groovy,都可以將函數作爲另一個函數的參數傳入,以便後者根據執行情況判斷是否要調用前者
或者能夠將一個函數作爲另一個函數的返回值返回,以便後續再對其進行調用
這種高階函數特性,就不要再說java的匿名內部類“能夠”實現了, 如果認爲匿名內部類已經”夠用”了的話,其實就已經與現在的話題“開發效率”相悖了

高階函數顯然是一種值得借鑑的特性,它會讓你少寫很多很多無聊的“包裝”代碼;

還有就是匿名函數(lambda)了
我不喜歡lambda、lambda地稱呼這個東西,我更喜歡把它叫做“匿名函數”或者“函數字面量(literal)”, 因爲它跟數學上的lambda演算還是有本質區別,叫”lambda”有誤導的危險

函數字面量的意思就是說,你可以在任何地方,甚至另一個函數體的調用實參或內部,隨時隨地地定義另一個新的函數
這種定義函數的形式,除了“這個函數我只想在這裏用一次,所以沒必要給它起個名字”這種理由之外,還有一個更重要的理由就是“閉包”了

所謂閉包,其實也是一個函數,但是在這個函數被定義時,其內部所出現的所有”自由變量(即未出現在該函數的參數列表中的變量)”已被當前外層上下文給確定下來了(lexical), 這時候,這個函數擁有的東西不僅僅是一套代碼邏輯,還帶有被確定下來的、包含那些“自由變量”的一個上下文, 這樣這個函數就成爲了一個閉包

那麼閉包這種東西有什麼好呢?其實如果懶散而鑽牛角尖地想,閉包的所有能力,是嚴格地小於等於一個普通的java對象的,也就是說,凡是可以用一個閉包實現的功能,就一定可以通過傳入一個對象來實現,但反過來卻不行 —— 因爲閉包只有一套函數邏輯,而對象可以有很多套,其次很多語言實現的閉包其內部上下文不可變但對象內部屬性可變

既然這樣,java還要閉包這種東西來幹嘛?其實這就又陷入了”匿名內部類可以實現高階函數”的困境裏了 —— 如果我在需要一個閉包的時候,都可以通過定義一個接口再傳入一個對象來實現的話,這根本就跟今天的話題“開發效率”背道而馳了

顯然,java是需要閉包的

強大而複雜的靜態類型系統

這和開發效率有關麼?
編程語言不是越“動態”,開發效率越高麼?還需要強大而複雜的靜態類型系統麼?

試想一下這種api定義:

def eat(foo) {
    ...
}

這裏面你認識的東西可能只有’吃’了, 你知道foo是什麼麼?你知道它想吃什麼麼?吃完後要不要產出點什麼東西? —— 你什麼都不知道
這種api極易調用出錯,這就好比我去買飯,問你想吃什麼你說“隨便”,但買回肯德基你卻說你實際想吃的是麥當勞一樣

可能你還會反駁說,不是還有文檔麼?你把文檔寫好點不就行了麼? —— 不要逼我再提“匿名內部類”的例子,如果給每個函數寫上覆雜詳盡的文檔是個好辦法,那就顯然 —— again, 與“開發效率”背道而馳了

那麼,靜態類型系統,這裏顯然就該用上了

靜態類型系統在多人協作開發、甚至團隊、組織間協作開發是非常有意義的;
擁有靜態類型系統的編程語言通常都有強大的、帶語法提示功能的IDE,這很正常,因爲靜態類型語言的語法提示功能好做;
只要把別人的庫拿過來,導入IDE,各種函數簽名只需掃一眼 —— 很多情況下根本不需要仔細看文檔 —— 就已經知道這個函數是幹嘛用的了, 合作效率成倍提升;

而且,作爲”api”,作爲“模塊邊界”,作爲與其它程序員合作的“門面”, 函數簽名上能將參數和返回值類型“卡”得越緊越好 —— 這樣別人不用猜你這個函數需要傳入什麼類型,甚至他在IDE裏一“點”,這裏就給自動填上了 :)

要做到“卡得緊”,光有靜態類型系統還不夠,這個系統還需強大, 試想一下這個例子:

/**
 * 我只吃香蕉和豬肉,請勿投食其它物品
 */
public void eat(List<Object> list) {
    for(Object o: list) {
        if(o instanceof Banana){
            ... // eating banana
        } else if(o instanceof Pork) {
            ... // eating pork
        } else {
            throw new RuntimeException("System err.");
        }
    }
}

這段純java代碼已經是“定義精確”的靜態類型了
但如果沒有上面那行註釋,你很可能會被System err.無數次
而這行註釋之所以是必需的,完全是因爲我找不到一個比List<Object>更好的表達“香蕉或豬肉”的形式, 這種情形足以讓人開始想念haskell的either monad

在“強大而複雜的類型系統”這一點上,jvm平臺上令人矚目的當屬scala了,可惜java沒有,這是值得借鑑的

不過這一點的“借鑑”還需java的compiler team發力,我等也只是說說(按照java保守的改進速度,估計HM類型系統是指望不上了)

動態類型系統,duck-typing

剛說完靜態類型,現在又來說動態類型系統合適麼?

然而這與節操無關,我想表達的是,只要是有助於“開發效率”的,都能夠借鑑,這是一個理性的java speaker的基本素質

我們在開發項目的時候,大量的編碼發生在“函數”或“方法”的內部 —— 這就好比你在屋子裏、在家裏宅着一樣, 是不是應該少一些拘束,多一些直截了當?
在這種情形下,動態類型系統要不要太爽? ——

Void visitAssert(AssertTree node, Void arg1) {
    def ahooks = this.hooks[VisitAssertHook.class]
    ahooks.each {it.beforeVisitCondition(node, errMsgs, this.ctx, resolveRowAndCol, setError)}
    scan((Tree)node.getCondition(), arg1);
    ahooks.each {it.afterVisitConditionAndBeforeDetail(node, errMsgs, this.ctx, resolveRowAndCol, setError)}
    scan((Tree)node.getDetail(), arg1);
    ahooks.each {it.afterVisitDetail(node, errMsgs, this.ctx, resolveRowAndCol, setError)}
    return null;
}

你知道ahooks是什麼類型麼?你不知道但我(我是編碼的人)知道
你知道ahooks身上有些什麼方法可以調麼?你同樣不知道但我知道

你不知道沒關係,只要我知道就行了,因爲現在是我在寫這段代碼;
這段代碼寫完以後,我只會把Void visitAssert(AssertTree node, Void arg1)這個類型明確的方法簽名提供給你調用,我並不會給你看函數體裏面的那坨東西,因此你知不知道上面這些真的沒關係

方法內部滿是def, 不用書寫繁複的List<Map<String, List<Map<Banana, Foo>>>>這種反人類反社會標語, 每個對象我知道它們身上能“點”出些什麼來,我只管“點”,跑起來之後invokedynamic會爲我搞定一切

動態類型系統 —— 這就是方法內部實現應該有的樣子
哪怕你的方法內部實現就是一坨shi,你也希望這坨shi能儘可能小隻一點,這樣看起來更清爽是吧?

不要說我太分裂,我要笑你看不穿 —— 靜態類型和動態類型既然都有好處,那麼他們能放在一起麼?
能的,這裏就需要點明這篇文章的政治目的了: “java與groovy混編”
而且,目前來看,jvm平臺上,只有它二者的結合,才能完成動態靜態混編的任務

曾經我發出過這樣一段感嘆:

公共api、對外接口聲明、應用程序邊界…這些對外的“臉面”部分代碼,如果擁有scala般強大的類型系統…就好了;而私有代碼、內部實現、各種內部算法、邏輯,如果擁有groovy般的動態、簡單的類型系統…就好了;綜上,如果有門語言,在接口和實現層面分別持有上述特性,就好了

這種“理想”中的語言或許某天我有空了會考慮實現一個

而現在,雖說不是scala,但我終於想要在java和groovy身上來試驗一把這種開發方式了
這裏我坦白一下爲什麼沒用scala,原因很簡單,我在技術選型方面是勢利的,scala還不被大多數平均水平的java開發人員(參見”工業化開發編程語言”定義第一條)接受,這直接導致項目的推進會遇到困難
而相對來講,我暫且相信大多數java開發人員都還算願意跨出groovy這一小步,當然這還需要時間證明

好了,下面還剩下一點點無關痛癢的牢騷 ——

元編程能力

macro, eval, 編譯過程切入, 甚至method missing機制,這些都算“元編程”

元編程能力的強弱直接決定了使用這種語言創作“內部DSL”的能力
java在元編程方面的能力,幾乎爲0

這是值得借鑑的

與groovy的混編,順便也能把groovy的元編程也帶進來

各種奇巧的語法糖

語法糖,關起門來吃最美味,這也是一種使得“方法內部實現更敏捷”的附加手段
網上隨便下載一份groovy的cheat sheet, 都會列舉groovy的那些寫代碼方面的奇技淫巧
這些奇技淫巧,在各種腳本語言之間其實都大同小異, 因爲他們本來就是抄來抄去的
結合方法內部的動態類型環境,這一定會進一步縮小方法內部實現代碼的體積

java & groovy混編:一種最“勢利”的折衷

我不去討論什麼語言纔是The True Heir of Java, 那會使這篇文章變成一封戰書,我只關心如何更好地利用現有開發資源完成項目,高效地幫組織實現利益
所以說java和groovy的混編是一種最“勢利”的折衷,我不想強迫平均水平的開發人員去學習一種完全不同的語言,短期內不會對項目有任何好處,真正想去學的人他自己會找時間去學
而groovy,說它是java++也不爲過,因爲java代碼直接就可以被groovy編譯, groovy完全兼容java語法, 對一般java開發人員來說,這真是太親切了

這裏我要提一下我對“java和groovy混編”的一個個人性質的小嚐試 —— kan-java項目

kan-java這個小工具,凡是用戶在編碼使用過程中能“碰”到的類和接口,全部都由java定義, 這確保用戶拿到的東西都有精確的類型定義

凡是對上述接口的實現,都以groovy代碼的形式存在

這貫徹了”接口靜態類型,內部實現動態類型”的宗旨, 或者說“凡是要提供給另外一個人看、調用的地方(接口或接口類),使用java,否則就用groovy”

當然了,單元測試也完全由groovy代碼實現

將kan-java的jar包引入到項目中使用時,就跟使用其它任何純java實現的jar包一樣 —— 接口清晰,參數類型明確,返回類型明確, 你不會也沒有必要知道開發人員在具體實現的時候,使用動態語言爽過一把

對於java和groovy的混編,項目的pom.xml如何配置,除了可以參考kan-java的配置外,還可以參考這個gist: https://gist.github.com/pfmiles/2f2ab77f06d48384f113, 裏面舉例了兩種配置方式,各有特色

具體的效果,還需要真正地去實際項目中體會
另外,kan-java也是一個有趣的工具,這個工具所實現的功能我也是從未見到java世界內有其它地方討論過的,它可以輔助java做“內部DSL”,有場景的可以一試

MAR 14TH, 2015 | COMMENTS

  1. 有大量平均水平左右的“工人”可被選擇、參與進來 —— 這意味着好招人
  2. 有成熟的、大量的程序庫可供選擇 —— 這意味着大多數項目都是既有程序庫的拼裝,標準化程度高而定製化場景少
  3. 開發工具、測試工具、問題排查工具完善,成熟 —— 基本上沒有團隊願意在時間緊、任務重的項目情況下去做沒有把握的、基礎開發工具類的技術試探
  4. 有面向對象特性, 適合大型項目開發 —— 無數大型項目已向世人述說,“面向對象”是開發大型軟件的優秀代碼組織結構
  5. 能適應大型團隊、多人協作開發 —— 代碼需要簡單易懂,起碼在接口、api層面是這樣

—— 這是我所理解的“工業化開發編程語言”的概念

很顯然, java就是種典型的“工業語言”, 非常流行,很多企業靠它賺錢,很實際;
但java也是常年被人黑,光是對其開發效率的詬病就已經足夠多,不過java始終屹立不倒;

這樣的局面其實無所謂高興還是擔憂,理性的程序員有很多種,其中一種是向“錢”看的 —— 我寫java代碼,就是因爲工作需要而已,能幫助我的組織搞定業務,做出項目,這很好;
當有人說java語言不好的時候,理性的程序員不會陷入宗教式的語言戰爭之中,他會思考這些人說的是否有道理;如果真的發現整個java平臺大勢已去,他會毫不猶豫地扭頭就走,不過直到目前爲止,還沒有這種跡象出現;

那麼,從這些無數次的口水之爭中,我們能否從別人的“戰場”上發現一些有用的東西, 來改進我們的開發方式,從而使得java這種已經成爲一個“平臺”的東西走得更遠,賺更多的錢呢?
答案是“有的”,感謝那些參與口水戰爭的、各種陣營的年輕程序員們,有了你們,java speaker們纔有了更多的思考;

我就只談一個最實際的問題:

java被吐槽的這些年, 就開發效率這一點而言,到底有哪些東西是值得借鑑的?

也就是說,到底是哪些主要特性直接導致了某些其它語言在語法上相對於java的優越感?

豐富的literal定義

在groovy中定義map和list的慣用方式:

def list = [a, 2 ,3]
def map = [a:0, b:1]

而java呢?只能先new一個list或map,再一個個add或put進去; 上面這種literal(字面量)形式的寫法便捷得多;

而javascript在這方面做得更絕, 我們都用過json,而json其實就是literal形式的object

極端情況下,一門編程語言裏的所有數據類型,包括”內建”的和用戶自定義的,統統可以寫成literal形式;
在這種情形下,其實這種語言連額外的對象序列化、反序列化機制都不需要了 —— 數據的序列化形式就是代碼本身, “代碼”和“數據”在形式上被統一了

java對這方面幾乎沒有任何支持,對於提高編碼效率來講,這是值得學習的一點, 起碼“內建”數據結構需要literal寫法支持

first-class function & higher-order function & function literal(lambda)

無論是js, 還是python/ruby,或是groovy,都可以將函數作爲另一個函數的參數傳入,以便後者根據執行情況判斷是否要調用前者
或者能夠將一個函數作爲另一個函數的返回值返回,以便後續再對其進行調用
這種高階函數特性,就不要再說java的匿名內部類“能夠”實現了, 如果認爲匿名內部類已經”夠用”了的話,其實就已經與現在的話題“開發效率”相悖了

高階函數顯然是一種值得借鑑的特性,它會讓你少寫很多很多無聊的“包裝”代碼;

還有就是匿名函數(lambda)了
我不喜歡lambda、lambda地稱呼這個東西,我更喜歡把它叫做“匿名函數”或者“函數字面量(literal)”, 因爲它跟數學上的lambda演算還是有本質區別,叫”lambda”有誤導的危險

函數字面量的意思就是說,你可以在任何地方,甚至另一個函數體的調用實參或內部,隨時隨地地定義另一個新的函數
這種定義函數的形式,除了“這個函數我只想在這裏用一次,所以沒必要給它起個名字”這種理由之外,還有一個更重要的理由就是“閉包”了

所謂閉包,其實也是一個函數,但是在這個函數被定義時,其內部所出現的所有”自由變量(即未出現在該函數的參數列表中的變量)”已被當前外層上下文給確定下來了(lexical), 這時候,這個函數擁有的東西不僅僅是一套代碼邏輯,還帶有被確定下來的、包含那些“自由變量”的一個上下文, 這樣這個函數就成爲了一個閉包

那麼閉包這種東西有什麼好呢?其實如果懶散而鑽牛角尖地想,閉包的所有能力,是嚴格地小於等於一個普通的java對象的,也就是說,凡是可以用一個閉包實現的功能,就一定可以通過傳入一個對象來實現,但反過來卻不行 —— 因爲閉包只有一套函數邏輯,而對象可以有很多套,其次很多語言實現的閉包其內部上下文不可變但對象內部屬性可變

既然這樣,java還要閉包這種東西來幹嘛?其實這就又陷入了”匿名內部類可以實現高階函數”的困境裏了 —— 如果我在需要一個閉包的時候,都可以通過定義一個接口再傳入一個對象來實現的話,這根本就跟今天的話題“開發效率”背道而馳了

顯然,java是需要閉包的

強大而複雜的靜態類型系統

這和開發效率有關麼?
編程語言不是越“動態”,開發效率越高麼?還需要強大而複雜的靜態類型系統麼?

試想一下這種api定義:

def eat(foo) {
    ...
}

這裏面你認識的東西可能只有’吃’了, 你知道foo是什麼麼?你知道它想吃什麼麼?吃完後要不要產出點什麼東西? —— 你什麼都不知道
這種api極易調用出錯,這就好比我去買飯,問你想吃什麼你說“隨便”,但買回肯德基你卻說你實際想吃的是麥當勞一樣

可能你還會反駁說,不是還有文檔麼?你把文檔寫好點不就行了麼? —— 不要逼我再提“匿名內部類”的例子,如果給每個函數寫上覆雜詳盡的文檔是個好辦法,那就顯然 —— again, 與“開發效率”背道而馳了

那麼,靜態類型系統,這裏顯然就該用上了

靜態類型系統在多人協作開發、甚至團隊、組織間協作開發是非常有意義的;
擁有靜態類型系統的編程語言通常都有強大的、帶語法提示功能的IDE,這很正常,因爲靜態類型語言的語法提示功能好做;
只要把別人的庫拿過來,導入IDE,各種函數簽名只需掃一眼 —— 很多情況下根本不需要仔細看文檔 —— 就已經知道這個函數是幹嘛用的了, 合作效率成倍提升;

而且,作爲”api”,作爲“模塊邊界”,作爲與其它程序員合作的“門面”, 函數簽名上能將參數和返回值類型“卡”得越緊越好 —— 這樣別人不用猜你這個函數需要傳入什麼類型,甚至他在IDE裏一“點”,這裏就給自動填上了 :)

要做到“卡得緊”,光有靜態類型系統還不夠,這個系統還需強大, 試想一下這個例子:

/**
 * 我只吃香蕉和豬肉,請勿投食其它物品
 */
public void eat(List<Object> list) {
    for(Object o: list) {
        if(o instanceof Banana){
            ... // eating banana
        } else if(o instanceof Pork) {
            ... // eating pork
        } else {
            throw new RuntimeException("System err.");
        }
    }
}

這段純java代碼已經是“定義精確”的靜態類型了
但如果沒有上面那行註釋,你很可能會被System err.無數次
而這行註釋之所以是必需的,完全是因爲我找不到一個比List<Object>更好的表達“香蕉或豬肉”的形式, 這種情形足以讓人開始想念haskell的either monad

在“強大而複雜的類型系統”這一點上,jvm平臺上令人矚目的當屬scala了,可惜java沒有,這是值得借鑑的

不過這一點的“借鑑”還需java的compiler team發力,我等也只是說說(按照java保守的改進速度,估計HM類型系統是指望不上了)

動態類型系統,duck-typing

剛說完靜態類型,現在又來說動態類型系統合適麼?

然而這與節操無關,我想表達的是,只要是有助於“開發效率”的,都能夠借鑑,這是一個理性的java speaker的基本素質

我們在開發項目的時候,大量的編碼發生在“函數”或“方法”的內部 —— 這就好比你在屋子裏、在家裏宅着一樣, 是不是應該少一些拘束,多一些直截了當?
在這種情形下,動態類型系統要不要太爽? ——

Void visitAssert(AssertTree node, Void arg1) {
    def ahooks = this.hooks[VisitAssertHook.class]
    ahooks.each {it.beforeVisitCondition(node, errMsgs, this.ctx, resolveRowAndCol, setError)}
    scan((Tree)node.getCondition(), arg1);
    ahooks.each {it.afterVisitConditionAndBeforeDetail(node, errMsgs, this.ctx, resolveRowAndCol, setError)}
    scan((Tree)node.getDetail(), arg1);
    ahooks.each {it.afterVisitDetail(node, errMsgs, this.ctx, resolveRowAndCol, setError)}
    return null;
}

你知道ahooks是什麼類型麼?你不知道但我(我是編碼的人)知道
你知道ahooks身上有些什麼方法可以調麼?你同樣不知道但我知道

你不知道沒關係,只要我知道就行了,因爲現在是我在寫這段代碼;
這段代碼寫完以後,我只會把Void visitAssert(AssertTree node, Void arg1)這個類型明確的方法簽名提供給你調用,我並不會給你看函數體裏面的那坨東西,因此你知不知道上面這些真的沒關係

方法內部滿是def, 不用書寫繁複的List<Map<String, List<Map<Banana, Foo>>>>這種反人類反社會標語, 每個對象我知道它們身上能“點”出些什麼來,我只管“點”,跑起來之後invokedynamic會爲我搞定一切

動態類型系統 —— 這就是方法內部實現應該有的樣子
哪怕你的方法內部實現就是一坨shi,你也希望這坨shi能儘可能小隻一點,這樣看起來更清爽是吧?

不要說我太分裂,我要笑你看不穿 —— 靜態類型和動態類型既然都有好處,那麼他們能放在一起麼?
能的,這裏就需要點明這篇文章的政治目的了: “java與groovy混編”
而且,目前來看,jvm平臺上,只有它二者的結合,才能完成動態靜態混編的任務

曾經我發出過這樣一段感嘆:

公共api、對外接口聲明、應用程序邊界…這些對外的“臉面”部分代碼,如果擁有scala般強大的類型系統…就好了;而私有代碼、內部實現、各種內部算法、邏輯,如果擁有groovy般的動態、簡單的類型系統…就好了;綜上,如果有門語言,在接口和實現層面分別持有上述特性,就好了

這種“理想”中的語言或許某天我有空了會考慮實現一個

而現在,雖說不是scala,但我終於想要在java和groovy身上來試驗一把這種開發方式了
這裏我坦白一下爲什麼沒用scala,原因很簡單,我在技術選型方面是勢利的,scala還不被大多數平均水平的java開發人員(參見”工業化開發編程語言”定義第一條)接受,這直接導致項目的推進會遇到困難
而相對來講,我暫且相信大多數java開發人員都還算願意跨出groovy這一小步,當然這還需要時間證明

好了,下面還剩下一點點無關痛癢的牢騷 ——

元編程能力

macro, eval, 編譯過程切入, 甚至method missing機制,這些都算“元編程”

元編程能力的強弱直接決定了使用這種語言創作“內部DSL”的能力
java在元編程方面的能力,幾乎爲0

這是值得借鑑的

與groovy的混編,順便也能把groovy的元編程也帶進來

各種奇巧的語法糖

語法糖,關起門來吃最美味,這也是一種使得“方法內部實現更敏捷”的附加手段
網上隨便下載一份groovy的cheat sheet, 都會列舉groovy的那些寫代碼方面的奇技淫巧
這些奇技淫巧,在各種腳本語言之間其實都大同小異, 因爲他們本來就是抄來抄去的
結合方法內部的動態類型環境,這一定會進一步縮小方法內部實現代碼的體積

java & groovy混編:一種最“勢利”的折衷

我不去討論什麼語言纔是The True Heir of Java, 那會使這篇文章變成一封戰書,我只關心如何更好地利用現有開發資源完成項目,高效地幫組織實現利益
所以說java和groovy的混編是一種最“勢利”的折衷,我不想強迫平均水平的開發人員去學習一種完全不同的語言,短期內不會對項目有任何好處,真正想去學的人他自己會找時間去學
而groovy,說它是java++也不爲過,因爲java代碼直接就可以被groovy編譯, groovy完全兼容java語法, 對一般java開發人員來說,這真是太親切了

這裏我要提一下我對“java和groovy混編”的一個個人性質的小嚐試 —— kan-java項目

kan-java這個小工具,凡是用戶在編碼使用過程中能“碰”到的類和接口,全部都由java定義, 這確保用戶拿到的東西都有精確的類型定義

凡是對上述接口的實現,都以groovy代碼的形式存在

這貫徹了”接口靜態類型,內部實現動態類型”的宗旨, 或者說“凡是要提供給另外一個人看、調用的地方(接口或接口類),使用java,否則就用groovy”

當然了,單元測試也完全由groovy代碼實現

將kan-java的jar包引入到項目中使用時,就跟使用其它任何純java實現的jar包一樣 —— 接口清晰,參數類型明確,返回類型明確, 你不會也沒有必要知道開發人員在具體實現的時候,使用動態語言爽過一把

對於java和groovy的混編,項目的pom.xml如何配置,除了可以參考kan-java的配置外,還可以參考這個gist: https://gist.github.com/pfmiles/2f2ab77f06d48384f113, 裏面舉例了兩種配置方式,各有特色

具體的效果,還需要真正地去實際項目中體會
另外,kan-java也是一個有趣的工具,這個工具所實現的功能我也是從未見到java世界內有其它地方討論過的,它可以輔助java做“內部DSL”,有場景的可以一試

關注公衆號,分享乾貨,討論技術


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