Java與groovy混編 —— 一種兼顧介面清晰和實現敏捷的開發方式
- 有大量平均水平左右的“工人”可被選擇、參與進來 —— 這意味著好招人
- 有成熟的、大量的程式庫可供選擇 —— 這意味著大多數專案都是既有程式庫的拼裝,標準化程度高而定製化場景少
- 開發工具、測試工具、問題排查工具完善,成熟 —— 基本上沒有團隊願意在時間緊、任務重的專案情況下去做沒有把握的、基礎開發工具類的技術試探
- 有面向物件特性, 適合大型專案開發 —— 無數大型專案已向世人述說,“面向物件”是開發大型軟體的優秀程式碼組織結構
- 能適應大型團隊、多人協作開發 —— 程式碼需要簡單易懂,起碼在介面、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”,有場景的可以一試
- 有大量平均水平左右的“工人”可被選擇、參與進來 —— 這意味著好招人
- 有成熟的、大量的程式庫可供選擇 —— 這意味著大多數專案都是既有程式庫的拼裝,標準化程度高而定製化場景少
- 開發工具、測試工具、問題排查工具完善,成熟 —— 基本上沒有團隊願意在時間緊、任務重的專案情況下去做沒有把握的、基礎開發工具類的技術試探
- 有面向物件特性, 適合大型專案開發 —— 無數大型專案已向世人述說,“面向物件”是開發大型軟體的優秀程式碼組織結構
- 能適應大型團隊、多人協作開發 —— 程式碼需要簡單易懂,起碼在介面、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”,有場景的可以一試