Go包导入与Java的差别

闲暇时翻阅了近期下载到的电子书《Go in Practice》 ,看到1.2.4 Package Management一节中的代码Demo,感觉作者对Go package导入的说法似乎不够精确:“Packages are imported by their name”(后续的说明将解释不精确的原因)。联想到前几天遇到的一个Java包导入的问题,让我隐约地感觉Java程序员很容易将两种语言的Package import机制搞混淆,于是打算在这里将Golang和Java的Package import机制做一个对比,对于Java转型到Golang的程序员将大有裨益:)。这里的重点在于与Java的对比,关于Golang的Package Import的细节可以参考我之前写过的一篇文章《理解Golang包导入》

我们先来看两个功能等价的代码。

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

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    fmt.Println(t.Format("2006-01-02"))
}

两个程序在Run时,都输出下面内容:

2016-9-13

我们看到Golang和Java都是用import关键字来进行包导入的:

import java.util.Date;

Date d = new Date();

vs.

import "time"

t := time.Now()

咋看起来,Java在package import后似乎使用起来更Easy,使用包内的类和方法时,前面无需再附着Package name,即Date d,而不是java.util.Date d。而Go在导入”time”后,引用包中方法时依然要附着着包名,比如time.Now()。但实质上两种语言在import package的机制上是有很大不同的。

1、机制

虽然都使用import,但import关键字后面的字符串所代表的含义有不同。

Java import导入的是类而不是包,import后面的字符串表示的是按需导入Java Package下面的类,比如import java.util.*; 或导入Package下某个类,比如import java.util.Date。而Go import关键字后面的字符串是包名吗?很多初学者会认为这个就是Go包名,实则不然,Go import后面的字符串实际上是一个包导入路径,这也是Java用”xxx.yyy.zzz”形式而Golang使用”xxx/yyy/zzz”形式的原因。我们用个简单的例子就能证明这一点。我们知道Golang会在\$GOROOT/src + \$GOPATH/src下面导入xxx/yyy/zzz路径下的包,我们在import “fmt”时,实际上导入的是\$GOROOT/src/fmt目录下的包,只是恰好这个下面的包的名字是fmt罢了。如果我们将\$GOROOT/src/fmt目录改名为fmt1,结果会是如何呢?

$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是一个helloworld go源码。

之所以出错是因为在\$GOROOT/src下已经没有fmt这个目录了,所以下面代码中的两个fmt含义是不同的(这也解释了Go in practice中关于包导入的说法的不精确的原因):

package main

import "fmt"  ---- 这里的fmt指的是$GOROOT/src下的名为"fmt"的目录名

func main() {
    fmt.Println("Hello, World") --- 这里的fmt是真正的包名"fmt"
}

从上面我们可以看出Go的包名和包的源文件所在的路径的名字并没有必须一致的要求,这也是为什么在Go源码使用包时一定是用packagename.XX形式,而不是packagename.subpackagename.XX的形式了。比如导入”net/http”后,我们在源码中使用的是http.xxx,而不是net.http.xxx,因为net/http只是一个路径,并不是一个嵌套的包名。

之所以看起来导入路径的终段目录名与包名一致,只是因为这是Go官方的建议:Go的导入路径的最后一段目录名(xxx/yyy/zzz中的zzz)与该目录(zzz)下面源文件中的Go Package名字相同。

下面是一个非标准库的包名与导入路径终段名完全不一致的例子:

//github.com/pkgtest/pkg1/foo.go
package foo

import "fmt"

func Foo() {
    fmt.Println("Foo in pkg1")
}

//testfoo.go
package main

import (
    "github.com/pkgtest/pkg1"
)

func main() {
    foo.Foo() //输出:Foo in pkg1
}

可以看出testfoo.go导入的是”github.com/pkgtest/pkg1″这个路径,但这个路径下的包名却是foo。

Java语言中的包实际以.jar为单位,.jar内部实际上也是以路径组织.class文件的,比如:foo.jar这个jar包中有一个package名为:com.tonybai.foo,foo包中包含类Foo、Bar,那实际上foo.jar内部的目录格式将是:

foo.jar
    - com/
        - tonybai/
            - foo/
                - Foo.class
                - Bar.class

但对于Java包的使用者,这些都是透明的。

2、重名

Java中关于包导入(实则是类导入)唯一的约束就是不能有两个类导入后的full name相同,如果存在两个导入类的full name完全相同,Javac在resolve时,要以ClassPath路径的先后顺序为准了,选择最先遇到的那个类。但是在Go中,如果导入的两个路径下的包名相同,那么Go compiler显然是不能允许这种情况的存在的,会给出Error信息。

比如我们在GOPATH下的github.com/pkgtest/pkg1和github.com/pkgtest/pkg2下放置了同名包foo,下面代码将会报错:

package main

import (
    "github.com/pkgtest/pkg1"
    "github.com/pkgtest/pkg2"
)

func main() {
    foo.Foo()
}

错误信息如下:

$go run testfoo.go
# command-line-arguments
./testdate.go:8: foo redeclared as imported package name
           previous declaration at ./testfoo.go:7

解决这一问题的方法就是采用package alias:

package main

import (
    a "github.com/pkgtest/pkg1"
    b "github.com/pkgtest/pkg2"
)

func main() {
    a.Foo()
    b.Foo()
}

编译执行上面程序将得到下面结果,而不是Error:

Foo of foo package in pkg1
Foo in foo package in pkg2

vim-go更新小记

自从上一次配置好Mac上的Golang Vim开发环境,基本上就没怎么动过。近两年过去了,Go已经升级到了1.7版本Vim-go截至目前也已经演化到了1.8版本了。社区的积极关注和使用,让Vim-go的作者Fatih Arslan备受鼓舞,于是近一年来,积极为vim-go添加新功能,发布新版本,并编写了vim-go的详细tutorial。这让我动了更新Vim-go版本的念头,于是就有了本篇内容。

已经记不得当初第一次配置vim-go时,vim-go的版本号是多少了。经过近两年的发展,vim-go已然正式成为Vim下Go开发环境的标准Plugin了。Go从当年的1.4升级到1.7,相关工具也跟着一起升级,比如oracle变成了guru,名字都换了。支持go的编辑器也逐渐增多并日益成熟,从最初vimliteIDE,到后来的eclipseIntelliJ Ideaatomsublime text以及vscode对golang都提供了支持。这样一来,无论你之前是哪种IDE的拥趸,你都能找到得心应手的环境走入Golang世界。

我个人一直用vim,sublime text3曾经玩过,没玩熟,卸了。目前机器上还装了一份vscode,感觉在IDE领域中,微软的影响力和成熟度不容小觑,vscode + golang extension从入门门槛来看,还是非常低的。即便vim-go进化到1.8版本,仍然不如vscode安装体验来得方便。当然这不全是vim-go的问题,而是vim的设计哲学所致。

无论是vim-go还是vscode golang plugin,都要依赖golang的周边工具,主要包括gocodegoimportsgurugodefgolintgometalinter等。在这方面,vim-go提供了安装依赖工具的方法“:GoInstallBinaries”,或在外部通过:vim -c “GoInstallBinaries” -c “qa”安装(在安装vim-go之后);而vscode则会自动探测其所依赖的工具是否安装,如果没有安装,会在vscode的下方给出提示,点击提示,会安装相应的工具。

BTW,自从近期golang官网:golang.org不用再翻墙后,go get下载golang.org域名下面的各种工具也简单了许多,大陆的Gopher们再也无需担心go package下载的问题了。

升级vim-go之前,建议先备份好.vimrc文件:

cp .vimrc .vimrc.bak.20160908

vim-go插件安装由很多方法,在vim-go tutorial中,vim-go作者选择了vim-plug,而没有用之前的vim插件管理工具vundle.vim,方法都是大同小异:

下载vim-plug:

$curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 67682  100 67682    0     0   7020      0  0:00:09  0:00:09 --:--:-- 12576

安装vim-go:

在.vimrc中填写如下内容:

call plug#begin()
Plug 'fatih/vim-go'

然后执行”:PlugInstall”即可。

在安装依赖工具期间,发现mac原生自带的vim(macvim,又叫mvim,安装在/usr/local/bin/mvim)版本还是7.3.xx版本,无法满足一些工具的要求,于是通过brew安装vim(安装在/usr/local/Cellar/vim/7.4.2334/bin/vim),然后通过/usr/bin/vim的一个符号链接连过去即可。

$ll /usr/bin|grep vim
lrwxr-xr-x     1 root   wheel        38  9  8 16:21 vim@ -> /usr/local/Cellar/vim/7.4.2334/bin/vim
... ...

注意,考虑要安装neocomplete以支持实时completion(补齐),vim需要有lua支持,因此执行brew install时要带上–with-lua参数:

brew install vim --with-lua

vim-go升级版安装后,可按照vim-go-tutorial中的步骤,体验一下vim-go的强大,同时对.vimrc进行相关配置,并安装缺失的vim插件,比如neocomplete、UltiSnips等。我针对vim-go 1.8配置好的.vimrc在这里可以下载到。

具体细节这里就不提了,如果还有哪些细节不清楚或实验没成功,可以回过头参考我那篇《Golang开发环境搭建-Vim篇》。

智慧城市到底满足的是谁的诉求

从当年IBM提出智慧城市概念算起,中国的第一轮智慧城市建设或多或少已经有个6、7年时间了。中国城市在智慧城市概念兴起之前,还切实做了数字城市、无线城市的建设,因此总体来看,中国城市在推进城市化和信息化的道路上已经走了相当长的一段时间了。当前中国城市正处于城市新一轮建设的机遇期,城市化被确定为中国未来经济发展的核心力量,是孕育中国经济发展新动能的关键所在。在这样一个关键时间节点上,我们对已经进行的城市建设进行深入反思是十分必要的,尤其是要对上一轮风风火火的智慧城市建设的效果进行细致分析,为今后的新一轮智慧城市建设提供值得借鉴的经验和教训。

今年是厄尔尼诺现象的极大年,入春以后,暴雨、台风、酷暑接踵而至,中华大地从南到北,从东到西,饱受着来自大自然神力的折磨。对于城市而言,这些自然灾害恰恰也是对之前城市建设成果的一次考验,无论是基础设施,还是城市综合运行管理和应急处理。而这场大考的评审官就是身处钢筋水泥城市中的市民、企业等城市日常活动的活跃主体。

这场评判的结果如何呢?这里用两句改自诗圣杜甫“赠花卿”的诗句来形容一下:“智慧城市只应天上有,人间能得几回闻”。也许这样的评价有些苛刻和过于严重了,但也恰恰能说明在上一轮智慧城市建设或是在某些城市正在进行的智慧城市建设中存在着许多不合理之处。这不禁让人想到一个问题:智慧城市到底满足的是谁的诉求!

一般来讲,城市中最根本诉求是民众的诉求,城市建设和发展都要以民众的诉求的满足为中心,以民为本。其他所有诉求都是建立在民众的诉求之上的,比如企业和政府的诉求。

但实际情况是如何呢?很多城市建设专家或国家部委负责人也在反思智慧城市建设的不足,他们总是谈到一点:“公众获得感不强烈”。“获得感不强”是一个什么概念呢,换句通俗的话,就是老百姓没有看到智慧城市建设的成果,民众需求实际上没有得到针对性的满足,老百姓的诉求没有得到解决或少有得到解决。怎么会发生这样的事情,我们的政府可是人民的政府,人民政府为人民,这是我们从小就接受到的教育。抱怨无用,我们来分析一下,为何民众的诉求没有得到很好的满足。

这一切都开始于城市的规划设计上,不过现在党中央以及地方各级政府都喜欢用一个更时髦的词汇:“顶层设计”。重视顶层设计的这种思路是没错的,这也是中国改革开放以来城市建设的经验总结。但如何做顶层设计,其实多数地方政府是没谱的、缺乏的甚至是不懂的,常见的做法就是将“顶层设计”作为一个价格不菲的工程,包给一个公司(比如IBM)或科研院所去做,最后弄出来一份成百甚至上千页的文档甚至是一套书来,这些东西就决定了这个城市的未来。

翻看一些试点城市的所谓顶层设计(或叫智慧城市总体规划,一般以5-10年为一个规划周期),我们满眼看到的都是一些高大上的重点项目(委以“智慧xx”的名头),看起来这些真的都是为民的项目。但这些项目所要解决的问题真的是市民们要解决的吗?这些顶层设计中城市目标、主要任务或是重点项目的确定是怎么来的呢?

一般来说,正确的做法应该是大量的调研和分析,但实际的做法更多是模仿和参考。我们不能否定一些公司可能做过大量的调研,但他们会将更多精力花在政府管理层以及政府各个委办局的调研上的,而不是普通民众。这样就会导致调研结果远离城市民众最核心的诉求,至少在民生改善方面,与民众那些看起来很“屌丝”的诉求间存在较大的Gap。同时,一些基于政府政绩诉求、上级领导喜好诉求或某些大型会议或赛事举办诉求的工程被摆在了台面之上,掩盖了民众最朴实的“屌丝”诉求;甚至一些不合格的顶层设计,由于copy成分的存在,将其他城市的述求变成了自己城市的诉求,这是一种多么“伟大”的精神啊!

在政府的伟大理想面前,其实民众的诉求反倒是很朴实的,但就是这些朴实的诉求却长年得不到满足,甚至某些问题因为某些顶设规划中项目的建设而变得恶化。这些事情就发生在你我身边:

比如:某市经过10年的建设,原先城市那些“知名”的积水点依然还是积水点,每降大雨必淹车。然后又因为扩城工程,城市又新增了若干个新积水点。这种情况如何让民众满足。如果说对积水点不改造,也有失偏颇。政府每年都会投入大量人力物力,实施各种整修工程,路重修重填,但无论路多新,修几次,水依旧照“积”不误。

再比如:某市因为之前的城市规划,工厂区搬到城郊,工人留在城内居民区,在居民区与厂区之间有一条关键的道路连接,这里成为每天上班族必经之路。但由于为了扩大“生态宜居”效应,政府硬是把原本没有水的地方引入了水系,于是好好的一条路被挖断了,路的下面挖出了一个水系,上面则变路为桥。这回周边的少数人倒是可以“宜居”了,但是每天因为桥的存在而在早晚高峰忍受拥堵的市民数量不知是前面人数的多少倍。

最后还有一个例子,因为某国家承办某重大赛事的要求,对赛事周边地区进行重新规划,为了所谓的带动那个地区的“人气”的要求,居然决定将原市博物馆、档案馆、科技馆和图书馆从原城区中心区域搬迁到三环外。 有人说北京的首都博物馆也在三环外附近,不过我告诉你,这里的三环外,相当于北京的6环外甚至更远,试想还有哪个城市有如此“大手笔”。这让以前那些经常逛图书馆、博物馆的有着阅读和文化娱乐需求的市民情何以堪啊,以上四馆的功能作用在搬迁后也势必大大降低。

以上两个例子也是顶层设计或是城市规划为未来设计(过多考虑未来需求,用IT人的说法叫“过设计”)、为少数人设计的典型案例。

再来看看交通拥堵问题,北上广深各大一线城市采用了诸多摇号、拍号、限购的方式尝试解决交通拥堵,但该堵还是堵,一点不给政府面子。那究竟是什么原因造成的堵呢。很多专家会说:原因是复杂的,多元的。但下面对话中反映出的这个原因也是导致很多大城市交通拥堵的重要甚至是根本原因之一:

问:你觉得为什么会堵?
答:都开车上班,车多。

问:why要开车?
答:单位远,无直通车或公共交通工具或公共交通工具太挤。

问:为什么单位里居住地那么远?
答:本来不远,搬迁走了。

问:为什么厂子要搬迁?
答:你说呢?这年头对地方政府而言啥最值钱你不知道么?

于是就有了每天早晚高峰的人口迁徙。

问:why不住在单位附近?
答:工厂有噪音、污染。

问:噪音、污染没人治理么?
答:你说呢?

从这个例子你也能看得出来,上述所谓的交通解决方案解决的都是皮毛和表层,我们可以大致看出地方政府在做规划时的出发点什么,结果就是:民众的潜在诉求被无情的放在了后面和低优先级的位置。很多所谓顶层设计中的决策和规划,不是站在解决民众诉求的角度,有些从长远去看,甚至是建立在损害民众利益的基础上的,这真的很可怕。

中央提出了城市顶层设计是正确无比的,但地方政府对顶层设计以及如何做顶层设计的深度认知需要时间。我们期望每个城市的智慧城市建设都能真正以满足民众的核心诉求为出发点,多多调研民众需求,从问题出发、从需求出发,从城市发展的目标出发,不要将其他城市的诉求copy为自身的诉求、不要有政绩考量的干扰、不要为过于长远的未来去过设计、不要以牺牲某些民众的利益去换取,实事求是,脚踏实地的去做。

理想总是美好的,现实却是骨感的,通往智慧的道路任重道远。




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn

文章

评论

  • 正在加载...

分类

标签

归档











更多