1. 程式人生 > >Java與groovy混編 —— 一種兼顧介面清晰和實現敏捷的開發方式

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”,有場景的可以一試

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