閱讀736 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《Groovy官方文檔》1.3 Groovy和Java比較

Groovy語言一直在努力親近Java開發人員。在設計Groovy語言的時候,我們遵循最小標新立異原則,努力讓那些Java開發背景的開發者容易上手並學會。下麵我們列舉Groovy和Java的一些主要區別。

1 默認導入

下麵的包和類是默認導入的,也就是說不必精確使用 import 語句來導入它們:

  • java.io.*
  • java.lang.*
  • java.math.BigDecimal
  • java.math.BigInteger
  • java.net.*
  • java.util.*
  • groovy.lang.*
  • groovy.util.*

2 動態方法(Multi-methods)

在Groovy裏,方法的調用是在運行時動態決定。這一特性叫做運行時分發(runtime dispatch)或動態方法(multi-methods)。也就是說方法的最後調用是根據傳入參數在運行時的類型所決定。在Java裏,這一點是不一樣的:在編譯時就決定了方法的參數類型。

下麵的代碼,我們采用Java風格,在Groovy和Java都可以編譯通過,但是運行結果不一樣:

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);

原因是Java使用的是靜態聲明的類型信息,這裏o被聲明為Object,但是Groovy是在運行時決定,當方法最終被調用時,因為這裏o實際是一個字符串,因此最終String版本的方法被調用。

譯者注:譯者之前也沒有接觸過Groovy語言,空閑時間也是有限的(姑且讓我找這個借口吧,雖然這個借口很牽強,對待知識本來應該以一種嚴謹,求真的態度)因此這裏有些專有名字可能翻譯不是很準確,比如對Multi-methods的翻譯。譯者也不確定是否準確,因此附帶了原文單詞,請讀者自行根據示例代碼和上下文意思理解。如果找到準確的中文翻譯懇請評論留言,以待修正。

3 數組初始化

在Groovy,{…}已經被用作閉包,也就是說你不能使用下麵的語法創建數組(譯者注:Java可以,並且很常用

int[] array = { 1, 2, 3}

你應該這樣聲明並初始化一個數組

int[] array = [1,2,3]

4 包範圍可見性(Package scope visibility)

在Groovy裏,省略字段的修飾符不會像Java一樣使其成為包私有屬性(package-private field)

class Person {
    String name
}

這裏,我們創建了一個屬性,它是私有的,而且自動關聯了getter和setter方法。如果我們要創建一個包私有屬性,可以添加@PackageScope注解來實現:

class Person {
    @PackageScope String name
}

5 ARM塊

ARM(Automatic Resource Management 自動資源管理)塊從Java7開始支持,但是Groovy不支持。相應地,Groovy依賴於閉包來實現類似的功能。示例:

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();
}

在Groovy裏可以寫成:

new File('/path/to/file').eachLine('UTF-8') {
   println it
}

或者也可以寫成跟Java類似風格:

new File('/path/to/file').withReader('UTF-8') { reader ->
   reader.eachLine {
       println it
   }
}

6 內部類

Groovy遵循了Java的匿名內部類以及嵌套內的特點。但是它並沒有完全依照Java語言規範,因此在使用前應該記住它們是有區別的。Groovy的實現和groovy.lang.Clouser類的風格有些類似,但也有不同點。比如在訪問私有字段和方法以及局部變量沒有final等。

6.1 靜態內部類

這是一個靜態內部類的例子:

class A {
    static class B {}
}

new A.B()

使用靜態內部類是一個非常好的實踐,如果你一定要使用內部類,建議優先考慮靜態內部類。

6.2 匿名內部類

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)

6.3 創建非靜態內部類實例

在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()語法,但你可以寫成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支持調用無參方法傳入一個參數。那個參數的值將會是null。這個特性對於調用構造函數同樣適用。可能會有人寫new X(this)而不是new X(),這是不合法的。雖然我們還沒有找到辦法避免用戶這樣寫。

7  拉姆達表達式

Java 8 支持拉姆達表達式和方法引用

Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);

Java8的拉姆達表達式或多或少被認為是匿名內部類。Groovy不支持這樣的語法,但是可以使用閉包代替:

Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)

8  GStrings

使用雙引號修飾的字符串被解釋為GString值。如果一個字符串裏含有美元符號在Groovy和Java的編譯器裏將會產生編譯錯誤。

當然,Groovy會自動在GString和String之間進行類型轉換,就像Java可以接受一個Object參數然後檢查其實際類型一樣。

9  字符串和字符

在Groovy裏,使用單引號修飾的被當成String類型,使用雙引號修飾的可以當成GString類型或String類型。取決於字麵常量。

assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString

如果聲明是char類型,Groovy會自動將單個字符從String類型轉換為char類型。如果被調用的方法聲明的參數類型是char,我們需要強製類型轉換為char類型。

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支持兩種風格的類型轉換,在轉換成char類型的時候,當個字符和多個字符轉換有些不一樣。對於多個字符轉換成char類型,Groovy會選擇第一個字符,這一點不像C語言,會直接失敗。

// 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'

10 ==的行為

在Java裏,==意味著基本類型相等或對象類型相等。在Groovy裏,==會轉換成a.compareTo(b)==0,如果他們是Comparable,就是使用a.equals(b),否則檢查基本類型,也就是is,比如a.is(b)

11  不同的關鍵字

Groovy比Java有更多的關鍵字,請不要把它們當變量名使用

  • in
  • trait

 

最後更新:2017-05-22 13:01:28

  上一篇:go  《Groovy官方文檔》1.2安裝Groovy
  下一篇:go  靜態工廠方法VS構造器