前言:
官方關于Programm Structure的介紹:Programm Structure
下文将會介紹Groovy的程式結構。
1.包名 Package names
這裡的包名同Java中的包名發揮着同樣的角色。包名允許我們分隔代碼進而避免沖突。Groovy類必須在定義之前指定他們的包,并且假設預設的包名存在。
定義包的方式和Java非常相似
// defining a package named com.yoursite
package com.yoursite
你可以調用Foo類使用com.yoursite.com.Foo,當然下面也将介紹import聲明的方式引用類。
2.導包 Imports
為了引用任意一個類,而不需要包名。Groovy遵從了Java的方式允許使用import聲明來解決類的導入。
例如,Groovy提供了幾個builder類,例如MarkupBuilder。MarkupBuilder類在groovy.xml包中,所有你可以使用該類,通過以下方式的導入:
// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder
// using the imported class to create an object
def xml = new MarkupBuilder()
assert xml != null
2.1. 預設導入 Default imports
預設導入時Groovy語言預設導入的。例如下面的代碼:
new Date()
同樣的代碼在Java中則需要導包聲明:java.util.Date。Groovy預設為你導入了這些類。
Groovy預設添加了以下導入:
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
之是以這樣做,是因為這些類非常常用。通過預設導入,減少了代碼量。
2.2. 簡單導包 Simple import
簡單導包是你使用類的全路徑進行導包。例如下面:
// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder
// using the imported class to create an object
def xml = new MarkupBuilder()
assert xml != null
2.3. 星号導入 Star import
像Java一樣,Groovy提供了一個特殊的方式導入包下的所有的類通過使用*,也就是所謂的星号導包。實行星号導包前的導入方式:
import groovy.xml.MarkupBuilder
import groovy.xml.StreamingMarkupBuilder
def markupBuilder = new MarkupBuilder()
assert markupBuilder != null
assert new StreamingMarkupBuilder() != null
使用星号導包後:
import groovy.xml.*
def markupBuilder = new MarkupBuilder()
assert markupBuilder != null
assert new StreamingMarkupBuilder() != null
2.4.靜态導包 Static import
在Groovy的靜态導包允許你導入類之後可以向靜态方法一樣在你自己的類中。和Java的靜态導入相似,但是在Java1.4之上才能正常工作。
用法和Java基本相似,不再贅述:
import static Boolean.FALSE
assert !FALSE //use directly, without Boolean prefix!
2.5.靜态導包别名 Static import aliasing
這種用法比較類似于C語言中的typedef的用法。
使用as關鍵字的靜态導包為命名空間問題提供了一個簡潔的解決方案。加入你想要獲得一個Calendar執行個體,使用getInstance()方法。它是靜态方法,是以你可以使用靜态導入。如果采用靜态發導入,在調用時将不帶類名是以容易誤導使用者。我們可以使用别名的形式進行導入,以增強閱讀性。
import static Calendar.getInstance as now
assert now().class == Calendar.getInstance().class
這樣變得非常簡潔易讀。
2.6.靜态星号導入 Static star import
星号靜态導入和規則的星号導入很相似。将會導入該類的所有的靜态的方法。
例如,下面的例子:
import static java.lang.Math.*
assert sin(0) == 0.0
assert cos(0) == 1.0
2.7 别名導入 Import aliasing
使用别名,我們可以使用自定義的名稱來指向一個完全的類。同上,可以通過as關鍵字來實作。
考慮以下類,假設由第三方的類庫提供的:
package thirdpartylib
public class MultiplyTwo {
def static multiply(def value) {
return value * 3 //intentionally wrong.
}
}
假如我們這樣使用這個庫:
def result = new MultiplyTwo().multiply(2)
現在假設有這樣一種情況,在使用了這個第三方庫并且貫穿了你所有的代碼,我們發現它沒有給出一個正确的結果。我們怎樣在一處修改,而不是修改源碼,而不改變其他使用的地方?Groovy提供了簡潔的方案。
使用簡單的别名導入,我們可以修複這個bug像這樣:
import thirdpartylib.MultiplyTwo as OrigMultiplyTwo
class MultiplyTwo extends OrigMultiplyTwo {
def multiply(def value) {
return value * 2 // fixed here
}
}
// nothing to change below here
def multiplylib = new MultiplyTwo()
// assert passes as well
assert 4 == new MultiplyTwo().multiply(2)
這就是通過as關鍵字重命名導入的類解決了這個問題。
别名導入在解決星号導入時的命名沖突同樣有用。
3.腳本vs類 Script versus classes
3.1. public static void main vs 腳本 public static void main vs script
Groovy同時支援class和script(即類和腳本)。使用一下代碼作為例子:
Main.groovy
class Main { //定義一個Main類,名字是任意的
static void main(String... args) {
println 'Groovy world!'
}
}
你會發現以上是Java中的典型代碼,代碼嵌入類中執行。而Groovy是的這些變得更加容易,以下代碼是等價的:
Main.groovy
println 'Groovy world!'
你可以把腳本想象成是一個不用聲明的類。
3.2 腳本類 Script class
腳本總是會被編譯成類。Groovy編譯器會為你編譯這個類,會把腳本體複制進run方法。上面的例子會被編譯如下:
Main.groovy
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script { //Main類繼承了groovy.lang.Script類
def run() { //groovy.lang.Script需要run方法傳回一個值
println 'Groovy world!'
}
static void main(String[] args) { //自動生成
InvokerHelper.runScript(Main, args)
}
}
如果腳本是一個檔案,檔案的基本名稱會被用于生成腳本類。在這個例子中,如果檔案名是Main.groovy,之後腳本類的會被命名為Main。
3.3. 方法Methods
定義方法在腳本中是可以的,如下:
int fib(int n) {
n < 2 ? 1 : fib(n-1) + fib(n-2)
}
assert fib(10)==89
你可以将方法和代碼混合。生成的腳本類會将所有的方法帶入腳本類,并且嵌入所有的腳步體到run方法中:
println 'Hello' //腳步開始處
int power(int n) { 2**n } //一個沒有腳本體的方法
println "2^6==${power(6)}"//腳本
以上代碼會被轉化成:
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
int power(int n) { 2** n}//power方法被複制進了自動生成的腳本類裡面
def run() {
println 'Hello' //腳本被複制到了這裡面
println "2^6==${power(6)}"
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
盡管Groovy通過你的腳本建立了一個類,不過這個類對使用者來說依然是易懂的。特别的,腳本被編譯成位元組碼,行号被保留。這就意味着如果腳本中出現了一個異常,異常記錄将會傳回相應的原始腳本的行号,而不是你自動生成類的行号。
3.4. 變量Variables
變量在腳本中不需要類型聲明。這就意味着,如下腳本:
int x = 1
int y = 2
assert x+y == 3
等同于:
x = 1
y = 2
assert x+y == 3
然而,二者之間在語義學上是不同的,表現如下:
- 如果聲明變量如同第一個例子,它是本地變量。将會被聲明在run方法,在腳本外部不可用。特别的,這種變量在腳本的其他方法中不可見。
- 如果變量沒有被聲明,變量會被綁定到腳本。并且對其他方法可見,你使用腳本與其他程式互動需要傳遞資料在腳本和程式之間,這種聲明是非常有用的。
如果你希望變量變成一個類的字段而不是在Binding中,你可以使用@Field annotation.