最近對GO語言産生了濃厚的興趣。因為GO語言不僅僅可以開發桌面、web程式,最吸引我的是安卓大有往GO語言全方位靠攏的趨勢,自家的系統還是用自家的語言開發比較靠譜。
用一句話來說:Go語言是谷歌2009釋出的第二款開源程式設計語言。 安卓也是谷歌的,這貨要鬧哪樣。
官方一點的解釋:Go語言是谷歌推出的一種全新的程式設計語言,可以在不損失應用程式性能的情況下降低代碼的複雜性。谷歌首席軟體工程師羅布派克(Rob Pike)說:“我們之是以開發Go,是因為過去10多年間軟體開發的難度令人沮喪。”
好吧說正事。
搭建GO語言開發環境參考這裡
Go語言環境搭建方法(Windows)
有興趣的同學一起學習吧。
QQ群:230931403
以下内容來自AllenDang<我為什麼喜歡Go語言>
簡潔的變量聲明和指派
拿最簡單的聲明變量和指派來看,下面這一句完成了聲明類型到指派,最後還有那個常見的分号作為語句的結束。
var i int = 10;
這個一點都不簡潔對吧?為什麼非要有“var”?為什麼不能自己推導變量類型?為什麼結尾非要加上分号?這三個問題,我相信Go語言的設計者也問過,并且都針對性的給了改進。重新來過。
i := 10
怎麼樣?“:=”是聲明并推導類型的文法糖,結尾的分号也省了,因為這裡我換行了,編譯器明白的。
還可以一次性聲明并指派多個變量。
i, j, k := 1, 2, 3
不同的類型也可以。
i, j, k := 1, 1.0, “hello”
如果要聲明一堆變量,但暫時不指派呢?可以這樣。
var (
i, j int
s string
u, v, s = 2.0, 3.0, "bar"
)
Go的設計者甚至覺得多打幾個“var”都不應該!
簡潔的if
有點意思了對吧?我學習一門新語言的時候,第一眼看變量類型和聲明,第二眼就會去看邏輯控制的文法。現在來看看都有些什麼?
if i > 10 {
println(“Greater then 10”)
}
稀松平常啊,難道一個簡單的if還能更簡單?恩,的确是的。首先if後面的條件判斷沒有人逼你再加上括号了,僅僅是少了兩次按鍵嘛,還有呢?還有!下面這個應該是很常見的if使用場景。
result := SomeMethod()
if result > 0 {
}
很多時候result這個變量其實僅僅用于條件判斷,完全可以在if之後就扔掉,是以Go有了這麼個寫法。
if result := SomeMethod(); result > 0 {
}
這個表達式太常用了,真是誰寫誰知道,每次我寫着一行都會心裡一爽。來看看糾結一點的if段。
if a {
} else if b {
} else if c {
} else {
}
這種寫法是可以的,但不是Go推薦的,理由是可以更簡潔。比如強悍的switch。
強悍的switch
這是很大家熟知的switch用法,注意,沒有break哦!Go裡面case之間不會“下穿”。
switch tag {
default:
s3()
case 0, 1, 2, 3:
s1()
case 4, 5, 6, 7:
s2()
}
神奇一點的switch,嘿嘿,與if異曲同工之妙。
switch x := f(); { // missing switch expression means "true"
case x < 0: return -x
default: return x
}
還有這個,有了這個更加明确的寫法,你真的還會if…else if…else if…else…嗎?
switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}
條件判斷舒服了,循環呢?
孤單的for
其實我一直不太明白,為什麼一門語言裡面要提供多個循環文法呢?for、while、do…while…都是不可替代的?用哪一個呢?似乎都是看個人愛好吧?可能大家随便就可以舉個例子出來證明這三個東西存在的必要和細微的差别,但對于我來說,做同一件事情如果有多種方法其實就是設計上的備援,會對使用者造成或多或少的困擾。來看看Go的循環吧。
for i := 0; i < 10; i++ {
}
for a < b {
}
for {
}
看吧,一個for就搞定所有情況了。來看一個常用的周遊集合,一把來說會寫成這樣。
count := len(someArray)
for i := 0; i < count; i++ {
println(someArray[i])
}
簡化這個,Go給出了一個關鍵字“range”,先看用法。
for i, value := range someArray {
// i 是整型,代表下标
// value就是數組内值的類型
}
range不單單可以用于數組,實際上它可以用于任何集合,比如map。
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for i, s := range a {
// type of i is int
// type of s is string
}
這裡隻是提到了幾點最基本的文法場景,Go裡面還有很多!
函數可以傳回多個值
其實能夠在一行多重指派的語言挺多的,但一個函數能傳回多個值的就很少了,比如在C#裡面如果要傳回兩個int,通常會這麼幹。
public class TwoInts
{
public int A;
public int B;
}
public class Foo
{
public TwoInts ReturnTwoInt();
}
然後就可以 TwoInts ti = foo.CalcTwoInt() 覺得悲催嗎?也許你都麻木了對嗎?很多語言都是這麼設計的。函數隻能傳回一個值最大的問題是會導緻出現很多沒必要的資料結構。上面就展現了這個備援,當然,你說可以用out關鍵字讓函數傳回,但這個文法用起來就不是那麼安全了。而這個問題在Go裡面解決起來太容易了,因為Go的函數可以傳回多個值!
func returnTwoInt() (int, int) {
}
a, b := returnTwoInt()
我對Go的好感就是從這裡萌芽的,這讓我的庫裡面從此少了很多資料結構!這無形中就能降低設計的複雜度。
函數内部聲明的對象指針可以安全的傳回
func ReturnPointer() *Object1 {
obj := new Object1()
obj.A = “hello”
return obj
}
Go的垃圾回收器會處理好這種情況的,放心啦!
異常處理?defer是啥?能吃嗎?
為什麼異常處理那麼複雜?多少人可以安全的實作下面這個邏輯?以下是僞代碼。
File f = File.Read(“c:\\text.txt”)
f.Write(xxx)
f.Close()
我相信,有經驗的碼農們腦子裡面瞬間出現了各種版本的try…catch…finally…,還有各種各樣的書寫規範,比如“catch”裡面的邏輯不能在抛異常之類的東西。其實想想,我們的要求很簡單,打開一個檔案,然後保證它在最後被關閉。僅此而已,為什麼做這麼簡單的一件事情非要那麼複雜?看看人家Go是怎麼做的!
func SaveSomething() {
if f, err := os.Open(“c:\\text.txt”); err == nil {
//各種讀寫
defer f.Close()
}
}
凡是加了defer的函數,都會在目前函數(這裡就是SaveSomething)執行完畢之後執行。就算“//各種讀寫”時發生異常f.Close也會堅定的在SaveSomething退出時被執行。有了這個,釋放點資源,關閉個把句柄這種小事再也無足挂齒!
接口再也不用“實作”了
從我接觸OO思想一來,凡是有接口的語言,都以不同的方式要求類“實作”接口,這樣的方式我一直都認為是天經地義的,直到我遇見了Go。
type Speaker interface {
Say()
}
上面定義了一個接口,隻有一個方法,Say,不需要參數,也沒有傳回值。Go裡面,任何擁有某個接口所定義所有方法的東西,都預設實作了該接口。這是一句擁有太多内涵的話,足矣對設計思路産生重大的影響。比如下面這個方法,它接受一個類型為Speaker的參數。
func SaySomething(s Speaker) {
s.Say()
}
那麼所有擁有Say()方法的東西都可以往裡扔。
在Go的世界裡,所有的東西都預設實作了interface{}這個接口。有了這個概念,即使沒有泛型也能有效的降低設計複雜度。
多線程還能更簡單點嗎?
要寫多線程,你要懂Thread,懂各種鎖,懂各種信号量。在各類系統裡面,“異步”邏輯通常代表“困難”。這是Go最強勁的部分,你見過比下面這個還簡單的異步代碼嗎(以下代碼摘自Go的官方範例)?
func IsReady(what string, minutes int64) { time.Sleep(minutes * 60*1e9); fmt.Println(what, "is ready") } go IsReady("tea", 6); go IsReady("coffee", 2); fmt.Println("I\'m waiting....");
執行的結果是,列印: I\'m waiting.... (right away) coffee is ready (2 min later) tea is ready (6 min later)
Go語言内置了“go”這個文法,任何go的方法,都将會被異步執行。那異步方法之前傳遞消息呢?用channel呗。意如其名,就是一個管道,一個往裡寫,另外一個等着讀。
ch := make(chan int) //建立一個隻能傳遞整型的管道
func pump(ch chan int) { for i := 0; ; i++ { ch <- i } //往管道裡寫值 }
func suck(ch chan int) { for { fmt.Println(<-ch) } //這裡會等着直到有值從管道裡面出來 }
go pump(ch) //異步執行pump
go suck(ch) //異步執行suck