為 Java 開發者準備的 Go 教程之Java 有而 Go 無
大家好,我是 polarisxu。
Go 語言的設計是站在巨人的肩膀上的,它吸取了其他語言的優秀設計,同時摒棄了一些「不認可」的設計。同時,為了保持簡單性,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 運算符具有不同的優先級。Go 的優先級更少,在我看來這更自然。如果不確定,請明確使用括號來指定優先級。一般來說,大家不用刻意去記這些優先級,有一個大概的印象即可。
但有一個關鍵的區別要記住,在 Go 中,i++ 和 i-- 是語句,而不是表達式。這是什么意思呢?語句就表明不能出現這樣惡心的寫法(常見的惡心面試題):
- // 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 時,經常 err != nil 時,傾向于用 panic 來中斷程序,不過正式代碼建議少用 panic。
4、While 和 Do 語句
while、do、for 是大部分語言提供的三大循環關鍵字。然而,Go 認為沒必要搞這么多關鍵字,直接一個 for 搞定。(雖然沒有直接替換 do 語句的,但肯定可以用 for 搞定)
- // 相當于 while (true) {}
- for {}
- // 相當于 while (x < 1) {}
- for x < 1 {}
- // ...
注意,Go 中的條件,包括 if 語句的,小括號可以省略,而且沒有糾結的 { 到底放在哪的問題,規定了只能放在末尾。
5、Throw 語句
Go 沒有 try/catch,因此也沒有 throw。硬要找一個類似的,那就是 panic,但思想是不一樣的。
6、Java 的一堆修飾符,Go 都沒有
比如 strictfp, transient, volatile, synchronized, abstract, static,這些關鍵字,Go 都沒有,也沒有類似的。大多數都是不需要的,因為 Java 中需要它們的問題在 Go 中以不同的方式得到解決。例如,通過將變量聲明為 package 級來實現與靜態值類似的效果。
7、對象、類、內部類、構造函數、this、super 等
Go 不像 Java 那樣完全支持面向對象編程(OOP)。因此,它不支持這些 Java 結構。但 Go 不少功能可以與大多數 OOP 功能類似使用,后續文章會講解。因此,Go 最好被描述為一種基于對象的語言。Go 允許實現 OOP 的一些關鍵目標,但與嚴格的 OOP 語言通常所采用的方式不同。最主要的是 Go 不支持繼承(雖然可以模擬類似繼承的功能),強調使用組合,因為繼承有點被亂用了。
Go 不支持類,也沒有構造函數(一般通過實現一個普通 New 函數充當構造函數),但有類似的功能,比如支持為類型定義方法,支持實現接口等。Go 的類型嵌套是組合,勉強有點類似 Java 的內部類。
Go 不需要顯示聲明實現哪個接口,而是一種隱式實現,大家通常稱為 duck type。
Go 沒有 this、super 等關鍵字。
8、函數式編程
雖然 Go 一開始就將函數定義為一等公民,但函數式相關功能支持不多,比如典型的實用函數(map、reduce、select、exclude、forEach、find 等),這是 Go 故意為之,主要考慮簡單性。隨著 Go 引入泛型,相關實用函數會考慮納入。
這方面,Java 也是后來才加入的。
注:Java5 開始支持泛型,Go 在 1.18 支持泛型。
9、基本類型包裝器
Java 集合(數組除外)不能包含基本類型值(primitive values,比如 int、long 等),只能包含對象。因此,Java 為每個基本類型提供包裝器類型。為了使集合更易于使用,Java 自動完成了這個包裝過程(box),以將其插入到集合中,并在從集合中取出值時展開(unbox)該值。Go 沒有這方面的限制。注意,需要使用裝箱(box/unbox)是 Java 在內存使用方面不如 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 沒有以上關鍵字,Go 只有導出和非導出。導出類似 public,通過首字母大寫來指定。首字母小寫則是未導出。
12、重載/重寫
在 Java 中,可以在同一范圍內定義具有相同名稱但具有不同簽名(不同數量和/或類型的參數)的函數。這被稱為(通過參數多態性的一種形式)重載函數。Go 不允許重載(overloaded)。
在Java中,具有相同名稱和簽名的函數可以在繼承層次結構的較低層重新定義。這種重新定義的函數被稱為(通過繼承多態性)重寫(overridden)。由于 Go 不支持繼承,因此不允許這種方式的重寫。不過 Go 中的嵌入類型,有類似重寫的功能。
肯定還有其他 Java 有而 Go 沒有的,歡迎交流!
參考
這個系列主要參考以下資料:
Go for Java Programmers
Java to Go in-depth tutorial
Go for Java Programmers: ebook