groovy比起java-有哪些地方寫起來更舒服
java發展緩慢,語法落后冗余
說起java,其實java挺好的,java現在的性能也不錯,但是,java的語法顯然比較落后,而且冗余,getter/setter之類的,5的泛型 for each,autobox,7的鑽石語法,try catch 多個exception,8的lambda,總之發展緩慢,9也跳票了.
groovy 是什么,有哪些特性
groovy是一個可選的動態類型語言,有靜態編譯的能力.在我看來,groovy寫起來很舒服,有一些動態語言的優勢,而且,它和java是100%兼容的,如果你使用靜態編譯,那么幾乎所有的特性也能和java互掉
先來幾個耳目一新的特性
int a=1//不需要分號
String multiLine='''
1//適應osc的markdwon
2//適應osc的markdwon
3'''//多行字符串
def map=[a:1,b:2]//這是個map
def list=[1,2,3,4,5]//這是個list
def range=1..5//這是個range
// null 空集合(包含map) 0 空字符串 空數組 在boolean環境中是false
def aMap=[:]
if (aMap){//groovy的false值,上面也提過
println('這段代碼不會被執行')//aMap意味着false
}
String interpolationString="$map or ${map}"//這是插值字符串,基本現代的語言都有吧
map << ['c':3]//這是重載運算符,實現起來其實很簡單
好了,先從這幾個小特性說起,兩個語言的對比范疇很大,但本文主要從語法的便利性角度入手,告訴你為什么groovy寫着舒服
1 可選分號;
大部分人可能覺得就是少寫個分號而已,我列到第一,大驚小怪.但是實際上我告訴你,你寫了那么多年的分號白寫了,實在沒啥用,kotlin已經列為可選了,我覺得這也算是現代語言的標志之一,去掉舊時代的冗余語法
分號沒用,因為大家寫java,基本都一行一句,沒必要加分號,編譯器自己加就完了(一行多句還是要自己加),不加分號也不影響代碼的可讀性,實際上完全不影響.我見到的現代語言中,rust需要分號來結尾,但是rust的分號是用來區別句子和表達式的,加分號是語句,不加分號是表達式(有返回值),是有真正的需求來區分語義的,但是java顯然不需要.
你java打了那么多年的分號,實際不需要,是浪費時間,我寫js的時候也不打分號,當然js的不打分號是有潛在的坑的,你需要知道,不過我這里不多說了
2 多行字符串和插值字符串
實際上,這是一種很迫切的需求,可惜java就沒有
比如我用spring jdbc,寫sql的時候,我需要很長的sql,而且這個sql我就用一次,很特殊,(沒必要用mybatis的方式抽出去),這個時候,我想怎么換行,就怎么換行,很隨意的.至於插值,這個明顯節約代碼量,省工.
'string'單引號閉合是String,"string${aaa}"雙引號閉合是GString,支持插值,目前知道這些就好了
我想,這也是java的mybatis之類的流行的一個原因吧,java不支持多行字符串,所以代碼里面寫sql有些虐
3 友好的map和list操作,自己看代碼就清楚了
def numList = [1, 2, 3, 4, 5]//很方便,等價List numList=new ArrayList();numList.add(1) ... 其實這個實現很簡單,但是java也許就是不願意做吧
def numMap = [a: 1, b: 2, c: 3]//很方便,等價也很方便
def list = [] //默認是ArrayList
assert list.class == ArrayList//可以省略.class
def map = [:] //默認是LinkedHashMap,為什么不是{},而是[:],官方的解釋就是{}和閉包還有語句的大括號沖突了,不過這也沒什么
assert map.class == LinkedHashMap
map.'a' = 1
map.b = 2
map << [c: 3]
assert map == [a: 1, b: 2, c: 3]
assert map.a == 1
assert map.'a' == 1//map 包含空白字符之類的可以用這種方式
assert map['a'] == 1//map 包含空白字符之類的可以用這種方式
list.add(1)
list << 2
assert list == [1, 2]
assert list[0] == 1
LinkedList list1 = []//因為默認是ArrayList,這種方式可以改變
assert list1.class == LinkedList
def list2 = [:] as LinkedList//另外一種方式改變
assert list2.class == LinkedList
def 我以后會講,你暫時需要知道的就是,你雖然用的是def,但是其實編譯器知道numList 是List
4 友好的操作符重載
現在先給你一個java的驚喜
Integer a = 1;
System.out.println(a == 1);//true無疑
BigDecimal b1 = new BigDecimal(1);
BigDecimal b2 = new BigDecimal(1);
b2.setScale(1);
System.out.println(b1 == b2);//竟然是false,好吧,這是對象引用的比較
System.out.println(b1.equals(b2));//是true
System.out.println(b1.compareTo(b2));//或許你需要用這個
換成groovy
BigDecimal a = 1
BigDecimal b = a + 1 - 1
b = b + 2 //還可以這樣寫
assert a == b//==被映射為equals
//用原來的== 可以用 .is
assert !(a.is(b))
int i = 1
if (i) {//其實(i)也是運算符重載,此時會調用i的asBoolean()方法,因為groovy會autobox基本類型
println('我會被執行,因為i==1,數字除了0都是true')
}
更多的重載運算符 重載運算符
5 去掉冗余的老舊的getter setter
groovy提出了新的概念properties,不是java的filed
class Pro {
Integer property1 //1 沒用private public protected(gorrvy里面是 @PackageScope)
def property2 //2 可選的statoc final(有final就會沒有set) 之類的
final String property3 //3 def 或者 確定的類型
static String property4 //4 property名字
//滿足以上四個條件就是一個property
//實際上等於java以下內容,一個private+getter+setter(final修飾的沒有setter)
private String property5
String getProperty5() {
return property5
}
void setProperty5(String property5) {
this.property5 = property5
}
}
groovy提出了property的概念,而且其實現也好java兼容,但是碼量明顯減少,其'.'的語法是GPath,會自動調用get
static void main(String[] args) {
def pro = new Pro()
Pro pro1 = [:]//也可以這樣聲明,前提是必須指定類型,並且有默認的構造器,以下等價
Pro pro2 = []//等價以上
Pro pro3 = [property1: 123]//等價以上
def pro4 = [property1: 123] as Pro//等價以上
pro.property1 = 123 //類似pro.setProperty1(123)
println(pro.property1)//類似pro.getProperty1()
}
6 動態語言的舒適,靜態語言的性能
groovy 1的是時候,有人詬病性能不佳,但是groovy 2開始,提供了@TypeChecked注解,靜態檢查,有時需要其他的注解來支持,@CompileStatic,靜態檢查+靜態編譯,此時會失去groovy的動態特性,而且此時的grrovy幾乎能實現和java 100%的互掉(不只是groovy 調用java,java 調用groovy 也一樣)
作為靜態語言的喜愛者,明顯是不用def,雖然groovy是可選的類型.官方的文檔也說到,現在動態也很快了,甚至有些時候比靜態都快,但是我不太信,我沒看到數據的對比.使用groovy,你可以用現代的語法,和類似動態語言的便利,寫出和靜態語言幾乎一樣的性能
@CompileStatic//開啟靜態編譯
class CompileStaticDemo {
static void main(String[] args) {
String s = '123'//雖然你用def,編譯器也知道s是String,但是這不利於代碼的閱讀,所以還是老老實實的用明確的類型
assert s.length() == 3
Closure square = {
int x ->
return x * x
}
assert 9 == square(3)
Pro pro = []
assert pro.property1 == null
//這點就是完全有靜態語言的寫法,而且編譯成靜態的代碼
}
}
7 豐富的內置支持,例如json
實際上groovy還是造了很多輪子的,比如groovy的模板,groovy版的jsp,交gsp,groovy內置的xml支持等等,這些輪子無疑會增加成本,但是很多我們可以不用,忽略即可
不過這個內置json,還是要提一下,這個內置json是整合的boon json,很早的一個評測,2013年的,boon json和其它框架的benchmark 那時候boon json的性能竟然大部分情況好於jackson
不管怎么說,java從5支持正則,沒有以前,java用正則要用jakarta 的正則包,你用groovy了以后,現在就可以用內置的json支持,還是很便利的
@CompileStatic
class JsonDemo {
static void main(String[] args) {
Pro pro = []
pro.property1 = 123
pro.property2 = 'property2'
pro.property5 = '555'
def json = JsonOutput.toJson(pro)
println(json)//{
"property5":"555","property2":"property2","property4":null,"property1":123,"property3":null
}
//null 值沒有忽略,groovy 2.5 中會添加支持,不斷完善中
}
}
我的實踐
groovy是一個動態語言,但是2.0以后,可以作為靜態語言來用,我覺得grrovy的動態特性可能主要是用來支持groovy DSL的,對於服務端的程序來說,我認為沒必要用它動態的特性.
但是每一個類都加@CompileStatic實在太麻煩,不過groovy提供了一個特性.自定義CompilerConfiguration ,你寫一個腳本
package conf
//換行
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
}
然后用下面的編譯,就好了
groovyc -configscript src/conf/config.groovy src/main/groovy/MyClass.groovy
當然我們的項目沒那么簡單,你用idea的話,可以配置這個Config script flag,用gradle的話,雖然我沒試過,但是我覺得也有類似的配置吧,所以你可以放心的用groovy的CompileStatic
用它代替java沒問題
1. 穩定的支持,發展好
groovy比起java,糖多,寫起來舒服,但是一個企業用的技術,必須是LTS,5年甚至10年都要有技術支持,這點grrovy能行嗎,實際上groovy 2.0以后,越來越好了,現在已經就入了apache,即使加入apache之前,也獲得過商業公司Pivotal軟件的支持,2015年3月,groovy加入的apache
這是tiobe的走勢 tiobe中groovy的走勢 也可以說明groovy的流行程度越來越高 tiobe groovy走勢
2. 沒有什么不可忍受的缺點
語法沒什么蹩腳的地方,這點和kotlin比比
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided
c = 3 // deferred assignment
我只想說,為啥不 int a =1 ,即使你能類型自動推測var a =1 也不利於閱讀,而且冒號也沒有空格好敲,不過,這個可以忍,
如果寫成下面這段代碼就更有意思了
好多var
你看到一大堆var的時候有何感想
3. 完全可以靜態檢查編譯
雖然它是動態類型,但是完全當靜態用沒問題,這點既保障了性能,也保證了代碼質量,上面已經提過了.當然用不用groovy,這個取決與你是否喜歡它,認為他比java好,寫起來更舒服
4. groovy即java,不缺輪子
java的生態groovy完全可以無縫繼承,而且靜態編譯的groovy幾乎也可以被java 100%互掉,所以輪子都是現成的,完全不缺
5. "0成本"的學習曲線
比起scala之流,groovy和java的相似度還是很大的,學習起來比較easy的,基本上基礎學幾下,用起來就沒什么問題了,不過你還是需要一段時間來適應