Go包导入与Java的差别
閑暇時(shí)翻閱了近期下載到的電子書《Go in Practice》?,看到1.2.4 Package Management一節(jié)中的代碼Demo,感覺作者對(duì)Go package導(dǎo)入的說法似乎不夠精確:“Packages are imported by their name”(后續(xù)的說明將解釋不精確的原因)。聯(lián)想到前幾天遇到的一個(gè)Java包導(dǎo)入的問題,讓我隱約地感覺Java程序員很容易將兩種語言的Package import機(jī)制搞混淆,于是打算在這里將Golang和Java的Package import機(jī)制做一個(gè)對(duì)比,對(duì)于Java轉(zhuǎn)型到Golang的程序員將大有裨益:)。這里的重點(diǎn)在于與Java的對(duì)比,關(guān)于Golang的Package Import的細(xì)節(jié)可以參考我之前寫過的一篇文章《理解Golang包導(dǎo)入》。
我們先來看兩個(gè)功能等價(jià)的代碼。
//TestDate.java import java.util.*; import java.text.DateFormat;public class TestDate {public static void main(String []args){Date d = new Date();String s = DateFormat.getDateInstance().format(d);System.out.println(s);} }和
//testdate.go package mainimport ("fmt""time" )func main() {t := time.Now()fmt.Println(t.Format("2006-01-02")) }兩個(gè)程序在Run時(shí),都輸出下面內(nèi)容:
2016-9-13我們看到Golang和Java都是用import關(guān)鍵字來進(jìn)行包導(dǎo)入的:
import java.util.Date;Date d = new Date();vs.
import "time"t := time.Now()咋看起來,Java在package import后似乎使用起來更Easy,使用包內(nèi)的類和方法時(shí),前面無需再附著Package name,即Date d,而不是java.util.Date d。而Go在導(dǎo)入”time”后,引用包中方法時(shí)依然要附著著包名,比如time.Now()。但實(shí)質(zhì)上兩種語言在import package的機(jī)制上是有很大不同的。
?
1、機(jī)制
雖然都使用import,但import關(guān)鍵字后面的字符串所代表的含義有不同。
Java import導(dǎo)入的是類而不是包,import后面的字符串表示的是按需導(dǎo)入Java Package下面的類,比如import java.util.*; 或?qū)隤ackage下某個(gè)類,比如import java.util.Date。而Go import關(guān)鍵字后面的字符串是包名嗎?很多初學(xué)者會(huì)認(rèn)為這個(gè)就是Go包名,實(shí)則不然,Go import后面的字符串實(shí)際上是一個(gè)包導(dǎo)入路徑,這也是Java用”xxx.yyy.zzz”形式而Golang使用”xxx/yyy/zzz”形式的原因。我們用個(gè)簡單的例子就能證明這一點(diǎn)。我們知道Golang會(huì)在\$GOROOT/src + \$GOPATH/src下面導(dǎo)入xxx/yyy/zzz路徑下的包,我們?cè)趇mport “fmt”時(shí),實(shí)際上導(dǎo)入的是\$GOROOT/src/fmt目錄下的包,只是恰好這個(gè)下面的包的名字是fmt罷了。如果我們將\$GOROOT/src/fmt目錄改名為fmt1,結(jié)果會(huì)是如何呢?
$go build helloworld.go helloworld.go:3:8: cannot find package "fmt" in any of:/Users/tony/.bin/go17/src/fmt (from $GOROOT)/Users/tony/Test/GoToolsProjects/src/fmt (from $GOPATH)helloworld.go是一個(gè)helloworld go源碼。之所以出錯(cuò)是因?yàn)樵赲$GOROOT/src下已經(jīng)沒有fmt這個(gè)目錄了,所以下面代碼中的兩個(gè)fmt含義是不同的(這也解釋了Go in practice中關(guān)于包導(dǎo)入的說法的不精確的原因):
package mainimport "fmt" ---- 這里的fmt指的是$GOROOT/src下的名為"fmt"的目錄名func main() {fmt.Println("Hello, World") --- 這里的fmt是真正的包名"fmt" }從上面我們可以看出Go的包名和包的源文件所在的路徑的名字并沒有必須一致的要求,這也是為什么在Go源碼使用包時(shí)一定是用packagename.XX形式,而不是packagename.subpackagename.XX的形式了。比如導(dǎo)入”net/http”后,我們?cè)谠创a中使用的是http.xxx,而不是net.http.xxx,因?yàn)閚et/http只是一個(gè)路徑,并不是一個(gè)嵌套的包名。
之所以看起來導(dǎo)入路徑的終段目錄名與包名一致,只是因?yàn)檫@是Go官方的建議:Go的導(dǎo)入路徑的最后一段目錄名(xxx/yyy/zzz中的zzz)與該目錄(zzz)下面源文件中的Go Package名字相同。
下面是一個(gè)非標(biāo)準(zhǔn)庫的包名與導(dǎo)入路徑終段名完全不一致的例子:
//github.com/pkgtest/pkg1/foo.go package fooimport "fmt"func Foo() {fmt.Println("Foo in pkg1") } //testfoo.go package mainimport ("github.com/pkgtest/pkg1" )func main() {foo.Foo() //輸出:Foo in pkg1 }可以看出testfoo.go導(dǎo)入的是”github.com/pkgtest/pkg1″這個(gè)路徑,但這個(gè)路徑下的包名卻是foo。
Java語言中的包實(shí)際以.jar為單位,.jar內(nèi)部實(shí)際上也是以路徑組織.class文件的,比如:foo.jar這個(gè)jar包中有一個(gè)package名為:com.tonybai.foo,foo包中包含類Foo、Bar,那實(shí)際上foo.jar內(nèi)部的目錄格式將是:
foo.jar- com/- tonybai/- foo/- Foo.class- Bar.class但對(duì)于Java包的使用者,這些都是透明的。
?
2、重名
Java中關(guān)于包導(dǎo)入(實(shí)則是類導(dǎo)入)唯一的約束就是不能有兩個(gè)類導(dǎo)入后的full name相同,如果存在兩個(gè)導(dǎo)入類的full name完全相同,Javac在resolve時(shí),要以ClassPath路徑的先后順序?yàn)闇?zhǔn)了,選擇最先遇到的那個(gè)類。但是在Go中,如果導(dǎo)入的兩個(gè)路徑下的包名相同,那么Go compiler顯然是不能允許這種情況的存在的,會(huì)給出Error信息。
比如我們?cè)贕OPATH下的github.com/pkgtest/pkg1和github.com/pkgtest/pkg2下放置了同名包foo,下面代碼將會(huì)報(bào)錯(cuò):
package mainimport ("github.com/pkgtest/pkg1""github.com/pkgtest/pkg2" )func main() {foo.Foo() }錯(cuò)誤信息如下:
$go run testfoo.go # command-line-arguments ./testdate.go:8: foo redeclared as imported package nameprevious declaration at ./testfoo.go:7解決這一問題的方法就是采用package alias:
package mainimport (a "github.com/pkgtest/pkg1"b "github.com/pkgtest/pkg2" )func main() {a.Foo()b.Foo() }編譯執(zhí)行上面程序?qū)⒌玫较旅娼Y(jié)果,而不是Error:
Foo of foo package in pkg1 Foo in foo package in pkg2? 2016,?bigwhite. 版權(quán)所有.
Related posts:
總結(jié)
以上是生活随笔為你收集整理的Go包导入与Java的差别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 平庸开发者的生存指南
- 下一篇: Redis 从入门到起飞(上)