天天看點

Groovy語言規範之程式結構1.包名 Package names2.導包 Imports3.腳本vs類 Script versus classes

前言:

官方關于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.

繼續閱讀