為 Java 開發(fā)者準備的 Go 教程之Java 有而 Go 無
大家好,我是 polarisxu。
Go 語言的設(shè)計是站在巨人的肩膀上的,它吸取了其他語言的優(yōu)秀設(shè)計,同時摒棄了一些「不認可」的設(shè)計。同時,為了保持簡單性,Go 的特性也比很多其他語言少。因此,Java 有一些特性,Go 沒有。但沒有,不代表不好。本文就看看具體有哪些。(當然,也存在 Go 有的特性,而 Java 沒有)
1、多重賦值
Java 可以在一條語句中將同一個值分配給多個變量(很多 C 族語言都支持)。例如:
- int x, y, z;
- x = y = z = 10;
Go 不支持上面的語法。相反,Go 采用另一種形式,有些時候更簡便。
- var x, y, z int = 10, 10, 10
而且,可以是不同類型:
- var x, y, z = 10, 12.0, "polarisxu"
正因為有這樣的語法,在 Go 中交換兩個變量的值很方便,不需要引入中間變量:
- var x, y = 1, 2
- x, y = y, x
2、語句和運算符
Go 和 Java 運算符具有不同的優(yōu)先級。Go 的優(yōu)先級更少,在我看來這更自然。如果不確定,請明確使用括號來指定優(yōu)先級。一般來說,大家不用刻意去記這些優(yōu)先級,有一個大概的印象即可。
但有一個關(guān)鍵的區(qū)別要記住,在 Go 中,i++ 和 i-- 是語句,而不是表達式。這是什么意思呢?語句就表明不能出現(xiàn)這樣惡心的寫法(常見的惡心面試題):
- // Go 中非法
- x = i++ + y
而且,Go 中根本沒有 --i 或 ++i。而 Java 是支持的。
Go 還不支持三元表達式。需要使用 if/else 語句代替。這點遭到很多人吐槽,畢竟大部分語言都支持。
- // Go 中編譯不通過
- z := x > y ? x : y
- // 得改為類似這樣:
- var z = y
- if x > y {
- z = x
- }
3、Assert 語句
Go 沒有 assert(斷言)語句。不過 Go 單元測試挺不錯的,一般會用測試來做類似的事情,而且也有一些好的測試框架支持 assert。在寫 Demo 時,經(jīng)常 err != nil 時,傾向于用 panic 來中斷程序,不過正式代碼建議少用 panic。
4、While 和 Do 語句
while、do、for 是大部分語言提供的三大循環(huán)關(guān)鍵字。然而,Go 認為沒必要搞這么多關(guān)鍵字,直接一個 for 搞定。(雖然沒有直接替換 do 語句的,但肯定可以用 for 搞定)
- // 相當于 while (true) {}
- for {}
- // 相當于 while (x < 1) {}
- for x < 1 {}
- // ...
注意,Go 中的條件,包括 if 語句的,小括號可以省略,而且沒有糾結(jié)的 { 到底放在哪的問題,規(guī)定了只能放在末尾。
5、Throw 語句
Go 沒有 try/catch,因此也沒有 throw。硬要找一個類似的,那就是 panic,但思想是不一樣的。
6、Java 的一堆修飾符,Go 都沒有
比如 strictfp, transient, volatile, synchronized, abstract, static,這些關(guān)鍵字,Go 都沒有,也沒有類似的。大多數(shù)都是不需要的,因為 Java 中需要它們的問題在 Go 中以不同的方式得到解決。例如,通過將變量聲明為 package 級來實現(xiàn)與靜態(tài)值類似的效果。
7、對象、類、內(nèi)部類、構(gòu)造函數(shù)、this、super 等
Go 不像 Java 那樣完全支持面向?qū)ο缶幊?OOP)。因此,它不支持這些 Java 結(jié)構(gòu)。但 Go 不少功能可以與大多數(shù) OOP 功能類似使用,后續(xù)文章會講解。因此,Go 最好被描述為一種基于對象的語言。Go 允許實現(xiàn) OOP 的一些關(guān)鍵目標,但與嚴格的 OOP 語言通常所采用的方式不同。最主要的是 Go 不支持繼承(雖然可以模擬類似繼承的功能),強調(diào)使用組合,因為繼承有點被亂用了。
Go 不支持類,也沒有構(gòu)造函數(shù)(一般通過實現(xiàn)一個普通 New 函數(shù)充當構(gòu)造函數(shù)),但有類似的功能,比如支持為類型定義方法,支持實現(xiàn)接口等。Go 的類型嵌套是組合,勉強有點類似 Java 的內(nèi)部類。
Go 不需要顯示聲明實現(xiàn)哪個接口,而是一種隱式實現(xiàn),大家通常稱為 duck type。
Go 沒有 this、super 等關(guān)鍵字。
8、函數(shù)式編程
雖然 Go 一開始就將函數(shù)定義為一等公民,但函數(shù)式相關(guān)功能支持不多,比如典型的實用函數(shù)(map、reduce、select、exclude、forEach、find 等),這是 Go 故意為之,主要考慮簡單性。隨著 Go 引入泛型,相關(guān)實用函數(shù)會考慮納入。
這方面,Java 也是后來才加入的。
注:Java5 開始支持泛型,Go 在 1.18 支持泛型。
9、基本類型包裝器
Java 集合(數(shù)組除外)不能包含基本類型值(primitive values,比如 int、long 等),只能包含對象。因此,Java 為每個基本類型提供包裝器類型。為了使集合更易于使用,Java 自動完成了這個包裝過程(box),以將其插入到集合中,并在從集合中取出值時展開(unbox)該值。Go 沒有這方面的限制。注意,需要使用裝箱(box/unbox)是 Java 在內(nèi)存使用方面不如 Go 高效的一方面原因。
10、Annotation(注解)
Go 沒有注釋。Go Struct 字段可以有標記(tag),這些標記提供類似但更有限的角色。
Annotation、function streams 和 lambda 使 Java(至少部分地)成為一種聲明性語言。Go 幾乎完全是一種命令式語言。這在有時候會使 Go 代碼更加冗長。
此外,Go 中的 build constraints 在某些方面和 Annotation 有類似的效果。
11、可見性
Java 支持四種可見性:
- private
- default
- protected
- public
Go 沒有以上關(guān)鍵字,Go 只有導(dǎo)出和非導(dǎo)出。導(dǎo)出類似 public,通過首字母大寫來指定。首字母小寫則是未導(dǎo)出。
12、重載/重寫
在 Java 中,可以在同一范圍內(nèi)定義具有相同名稱但具有不同簽名(不同數(shù)量和/或類型的參數(shù))的函數(shù)。這被稱為(通過參數(shù)多態(tài)性的一種形式)重載函數(shù)。Go 不允許重載(overloaded)。
在Java中,具有相同名稱和簽名的函數(shù)可以在繼承層次結(jié)構(gòu)的較低層重新定義。這種重新定義的函數(shù)被稱為(通過繼承多態(tài)性)重寫(overridden)。由于 Go 不支持繼承,因此不允許這種方式的重寫。不過 Go 中的嵌入類型,有類似重寫的功能。
肯定還有其他 Java 有而 Go 沒有的,歡迎交流!
參考
這個系列主要參考以下資料:
Go for Java Programmers
Java to Go in-depth tutorial
Go for Java Programmers: ebook