Groovy學(xué)習(xí)目錄-傳送門(mén)
Groovy試圖對(duì)Java開(kāi)發(fā)人員盡可能自然。 我們?cè)噲D在設(shè)計(jì)Groovy時(shí)遵循最小驚訝原則,特別是對(duì)于來(lái)自Java背景的Groovy開(kāi)發(fā)人員。
這里我們列出了Java和Groovy之間的所有主要區(qū)別。
默認(rèn) imports
所有這些包和類都是默認(rèn)導(dǎo)入的,您不必使用顯式import語(yǔ)句來(lái)使用它們:
java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
java.net.*
java.util.*
groovy.lang.*
groovy.util.*
Multi-methods
在Groovy中,將在運(yùn)行時(shí)選擇將被調(diào)用的方法。 這稱為運(yùn)行時(shí)分派或Multi-methods。 這意味著將基于運(yùn)行時(shí)參數(shù)的類型來(lái)選擇方法。 在Java中,則是根據(jù)聲明的類型,在編譯時(shí)選擇方法。
下面的代碼,以Java代碼編寫(xiě),可以在Java和Groovy中編譯,但它的行為會(huì)有所不同:
int method(String arg) {
return 1;
}
int method(Object arg) {
return 2;
}
Object o = "Object";
int result = method(o);
在Java中, 您講得到:
assertEquals(2, result);
而在Groovy中:
assertEquals(1, result);
這是因?yàn)镴ava將使用靜態(tài)信息類型,即o被聲明為Object,而Groovy將在運(yùn)行時(shí)選擇該方法被實(shí)際調(diào)用時(shí)。 因?yàn)樗怯?code>String調(diào)用的,所以調(diào)用String版本。
數(shù)組初始化
在Groovy中,{...}塊是為閉包而保留的。 這意味著您不能使用以下語(yǔ)法創(chuàng)建數(shù)組literal:
int [] array = {1,2,3}
實(shí)際上你必須使用:
int [] array = [1,2,3]
包范圍可見(jiàn)性
在Groovy中,在字段上省略修飾符不會(huì)像Java中一樣變成package-private字段:
class Person {
字符串名稱
}}
相反,它用于創(chuàng)建一個(gè)屬性,也就是說(shuō)一個(gè)私有字段,一個(gè)關(guān)聯(lián)的getter和一個(gè)關(guān)聯(lián)的setter。
可以通過(guò)使用@PackageScope注釋來(lái)創(chuàng)建一個(gè)package-private字段:
class Person {
@PackageScope字符串名稱
}}
自動(dòng)資源管理塊
Groovy不支持Java 7中的ARM(自動(dòng)資源管理)塊。 相反,Groovy提供了依賴閉包的各種方法,它們具有相同的效果,同時(shí)更加慣用。 例如:
Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
可以這樣寫(xiě):
new File('/path/to/file').eachLine('UTF-8') {
println it
}
或者,如果你想要一個(gè)更接近Java的版本:
new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}
內(nèi)部類
匿名內(nèi)部類和嵌套類的實(shí)現(xiàn)遵循Java方式,但是你不應(yīng)該拿出Java語(yǔ)言規(guī)范,并且對(duì)不同的東西不斷地?fù)u頭。 它的實(shí)現(xiàn)看起來(lái)很像groovy.lang.Closure,同時(shí)有一些好處和一些差異。 例如訪問(wèn)私有字段和方法可能成為一個(gè)問(wèn)題,但另一方面,局部變量不必是final的。
- 靜態(tài)內(nèi)部類
這里有一個(gè)靜態(tài)內(nèi)部類的例子:
class A {
static class B {}
}
new A.B()
靜態(tài)內(nèi)部類的使用是最好的支持。 如果你需要一個(gè)內(nèi)部類,你最好使用靜態(tài)的。
- 匿名內(nèi)部類
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
CountDownLatch called = new CountDownLatch(1)
Timer timer = new Timer()
timer.schedule(new TimerTask() {
void run() {
called.countDown()
}
}, 0)
assert called.await(10, TimeUnit.SECONDS)
- 創(chuàng)建非靜態(tài)內(nèi)部類的實(shí)例
在Java中,你可以這樣做:
public class Y {
public class X {}
public X foo() {
return new X();
}
public static X createX(Y y) {
return y.new X();
}
}
Groovy不支持y.new X()語(yǔ)法。 相反,你必須用new X(y),如下面的代碼:
public class Y {
public class X {}
public X foo() {
return new X()
}
public static X createX(Y y) {
return new X(y)
}
}
注意,Groovy支持使用一個(gè)參數(shù)調(diào)用方法,而不提供參數(shù)。 然后,參數(shù)的值為null。 基本上,相同的規(guī)則適用于調(diào)用構(gòu)造函數(shù)。 有一個(gè)危險(xiǎn),例如你會(huì)寫(xiě)new X(),而不是new X(this)。 由于這也可能是正常的方式,我們還沒(méi)有找到一個(gè)好的方法來(lái)防止這個(gè)問(wèn)題。
Lambdas
Java 8支持lambdas和方法引用:
Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);
Java 8 lambdas可以或多或少被認(rèn)為是匿名內(nèi)部類。 Groovy不支持該語(yǔ)法,但是可以使用閉包:
Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)
GStrings
由于雙引號(hào)字符串字面量被解釋為GString,如果具有包含美元字符的String的類是使用Groovy和Java編譯器編譯的,Groovy可能會(huì)失敗并產(chǎn)生編譯錯(cuò)誤或產(chǎn)生細(xì)微不同的代碼。
通常,Groovy將在GString和String之間自動(dòng)轉(zhuǎn)換,如果API聲明參數(shù)的類型,小心接受Object參數(shù)的Java API,然后檢查實(shí)際的類型。
字符串和字符
Groovy中的單引號(hào)用于String,雙引號(hào)結(jié)果是String或GString,取決于文字中是否有插值。
assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString
只有在賦給char類型的變量時(shí),Groovy會(huì)自動(dòng)將單字符String轉(zhuǎn)換為char。 當(dāng)調(diào)用類型為char的參數(shù)的方法時(shí),我們需要顯式轉(zhuǎn)換或確保該值已預(yù)先轉(zhuǎn)換。
char a='a'
assert Character.digit(a, 16)==10 : 'But Groovy does boxing'
assert Character.digit((char) 'a', 16)==10
try {
assert Character.digit('a', 16)==10
assert false: 'Need explicit cast'
} catch(MissingMethodException e) {
}
Groovy支持兩種類型的轉(zhuǎn)換,在轉(zhuǎn)換為char的情況下,在轉(zhuǎn)換multi-char 時(shí)存在微妙的差別。 Groovy風(fēng)格的轉(zhuǎn)換是更寬松的,將采取第一個(gè)字符,而C風(fēng)格的轉(zhuǎn)換將失敗,異常。
// for single char strings, both are the same
assert ((char) "c").class==Character
assert ("c" as char).class==Character
// for multi char strings they are not
try {
((char) 'cx') == 'c'
assert false: 'will fail - not castable'
} catch(GroovyCastException e) {
}
assert ('cx' as char) == 'c'
assert 'cx'.asType(char) == 'c'
原始和封裝
因?yàn)镚roovy使用Objects來(lái)做每一件事,它對(duì)原始的引用自動(dòng)包裝。 因此,它不遵循Java的行為擴(kuò)展優(yōu)先于裝箱。 這里有一個(gè)使用int的例子
int i
m(i)
//這是Java將調(diào)用的方法,因?yàn)閿U(kuò)展優(yōu)先于拆箱。
void m(long l) {
println "in m(long)"
}
//這是Groovy實(shí)際調(diào)用的方法,因?yàn)樗械幕疽枚际褂盟鼈兊陌b類。
void m(Integer i) {
println "in m(Integer)"
}
==的行為
在Java中==表示對(duì)象的原始類型或標(biāo)識(shí)的相等性。 在Groovy ==翻譯為a.compareTo(b)== 0,如果他們是可比較的,否則a.equals(b)。 如果要檢查身份,有is方法,例如a.is(b)。
轉(zhuǎn)換
額外的關(guān)鍵字
Groovy中還要比Java多幾個(gè)關(guān)鍵字。 不要將它們用于變量名稱等。
asdefintrait