尝试Java,从入门到Kotlin(上)
之前一直使用C#開發(fā),最近由于眼饞Java生態(tài)環(huán)境,并借著工作服務(wù)化改造的契機(jī),直接將新項(xiàng)目的開發(fā)都轉(zhuǎn)到Java上去。積攢些Java開發(fā)經(jīng)驗(yàn),應(yīng)該對(duì).NET開發(fā)也會(huì)有所啟發(fā)和益處。
從理論上說,Java和C#語言差別不大,畢竟難聽地說,C#就是抄Java出來的。程序語言簡史如是介紹這兩種語言:
然而隨著時(shí)間流逝語言發(fā)展,個(gè)人認(rèn)為,C#在語言層面已經(jīng)大大領(lǐng)先了Java。關(guān)于Java和C#的比較這幾篇文章http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-1-thoughts-and-goals.html有著詳細(xì)的描述。下面我總結(jié)一下我在趟過的坑,以供轉(zhuǎn)型或?qū)W習(xí)的同學(xué)參考。
本文并非要比出這些語言誰優(yōu)誰劣。有時(shí)候,好或壞是非常主觀的判斷,不同人有著不同的看法,強(qiáng)行斷定好壞只會(huì)引起無畏的爭論。這些語言有著各自的特點(diǎn),有各自適合的場(chǎng)景。就像下面要談到的Checked Exception特性,這是個(gè)很好的特性,但是在一些情況下也會(huì)引起不少麻煩。
Checked Exception
Java是Checked Exception的。這就是說,如果你寫了一個(gè)方法,這個(gè)方法會(huì)拋出一些異常,那么你需要用throws關(guān)鍵字標(biāo)明這個(gè)方法會(huì)拋出哪些異常。這個(gè)特性很難說是好還是不好。Checked Exception本質(zhì)上是一種類型系統(tǒng),它明確規(guī)定了一個(gè)方法除了返回值類型以外,還可能拋出什么異常。這樣調(diào)用方函數(shù)就能夠明確地知曉應(yīng)該處理或者傳遞哪些異常。這個(gè)特性在用得好的人手里,對(duì)正確處理各種邊邊角角的異常十分有用。然而,如果在你無法自己選隊(duì)友,無法控制開發(fā)人員的水平的情況下,你很可能會(huì)發(fā)現(xiàn),所有的方法都被標(biāo)記為throws Exception。
Lambda,以及與Checked Exception產(chǎn)生的奇怪反應(yīng)
Java的Lambda本質(zhì)上仍然是一個(gè)對(duì)象。事實(shí)上,Java的Lambda函數(shù)是一個(gè)滿足Functional Interface接口的對(duì)象。比如下面代碼,聲明了一個(gè)具有一個(gè)int參數(shù),返回一個(gè)int參數(shù)的函數(shù)。
@FunctionalInterface
interface AFunction {int invokeBalaBala(int a);
} 我們可以這樣定義一個(gè)這個(gè)函數(shù)的變量:AFunction f = x -> 2 * x;。
Java的Lambda和Checked Exception結(jié)合在一起后,產(chǎn)生了一個(gè)非常棘手的問題。由于Checked Exception是類型系統(tǒng)的一部分,一個(gè)不拋出異常的函數(shù)和一個(gè)會(huì)拋出異常的函數(shù),它們的類型是不相同的。這就導(dǎo)致了Java的Lambda泛用性大大減少而且不是很好用。以對(duì)List的map操作為例,我們可以用如下代碼將list里的每個(gè)元素翻倍:
list = list.stream().map(x -> 2 * x).collect(Collectors.toList()); 這里map接收一個(gè)類型為輸入一個(gè)int參數(shù),返回一個(gè)int值的函數(shù)。然而,如果我們需要給它的函數(shù)有可能拋出異常,比如這個(gè)函數(shù)會(huì)去讀取文件、訪問網(wǎng)絡(luò)服務(wù)、或者做Json反序列化,則由于類型不同,Java編譯器將會(huì)報(bào)錯(cuò)。
// 這個(gè)編譯器會(huì)報(bào)錯(cuò)
list.stream().map(x -> JsonUtil.parse(x)).collect(Collectors.toList()); 解決方案一種是在函數(shù)體中使用try cache處理異常。但是很多時(shí)候,異常沒辦法在這個(gè)時(shí)刻處理,必須要拋出。那么還有另一種方案:將異常轉(zhuǎn)換為RuntimeException,RuntimeException是所謂的Unchecked Exception,它不是類型系統(tǒng)的一部分,不需要用throws標(biāo)注,所以不會(huì)導(dǎo)致函數(shù)類型變化。另一方面,編譯器也無法檢測(cè)出是否可能會(huì)拋出RuntimeException。無論采用哪種方案,都使得這個(gè)Lambda函數(shù)變得沒那么好看。
泛型
Java的泛型原理和C#不同。C#是運(yùn)行時(shí)泛型,在程序運(yùn)行的時(shí)候仍然能獲取泛型的類型信息。而Java的泛型是類型擦除(Type Erasure)式泛型。名稱聽起來很高大上,意思是Java的泛型僅僅用于編譯時(shí)類型檢查,類型檢查完成后,類型信息就被編譯器擦除。在最后生成的字節(jié)碼中中,泛型類型都被改為Object類型。
比如這句:
HashMap<TK, TV> map = new HashMap<TK, TV>(); 編譯后變成:
HashMap map = new HashMap(); Type Erasure方式的影響主要有兩個(gè):
- 運(yùn)行時(shí)無法判斷類型;
- 運(yùn)行時(shí)無法動(dòng)態(tài)生成泛型具現(xiàn)化的類的實(shí)例。
像下面兩句:
x instanceof T
new T() 在Java中都會(huì)編譯出錯(cuò)。而這在C#中都是很常見的代碼。在C#中,我們可以有這樣的Json反序列化方法:
T parse<T>(string jsonStr) 這個(gè)方法將jsonStr反序列化為類型T的一個(gè)對(duì)象。這種寫法看起來十分自然。然而在Java中無法實(shí)現(xiàn)。因?yàn)樵?code>parse方法中需要在運(yùn)行時(shí)實(shí)例化T的一個(gè)對(duì)象,而Java在運(yùn)行時(shí)這些泛型都已經(jīng)被擦除,無法獲取類型T的信息,從而無法實(shí)例化。要在Java實(shí)現(xiàn)類似的方法,需要額外將一個(gè)Class對(duì)象放到參數(shù):
T parse(String jsonStr, Class<T> type) 這樣Java才能使用這個(gè)type,在運(yùn)行時(shí)使用反射的方式生成類型T的實(shí)例。
Getter/Setter
在面向?qū)ο笳軐W(xué)中,字段屬于實(shí)現(xiàn)細(xì)節(jié),應(yīng)該設(shè)為private使它隱藏在類的內(nèi)部。但是在實(shí)際中,有很多字段需要直接訪問和修改。從功能實(shí)現(xiàn)上講,直接把字段設(shè)為public也是可以的。但是這樣做的壞處在于未來功能擴(kuò)展時(shí),這個(gè)字段的含義、存儲(chǔ)方式可能發(fā)生變化,導(dǎo)致每個(gè)使用了這個(gè)字段的代碼都需要修改。因此,應(yīng)該將字段的訪問封裝的方法中,即使只是很簡單的訪問和設(shè)置,也應(yīng)該實(shí)現(xiàn)getter方法和setter方法。
C#和Python有property特性支持快速定義和調(diào)用getter方法和setter方法。Ruby則依靠函數(shù)調(diào)用可以省略括號(hào)的特性,使getter方法看起來很像直接訪問字段。Java沒有使用特性支持getter和setter方法,而是約定必須實(shí)現(xiàn)字段名前加get的getter方法(然而這里有個(gè)不一致的地方,如果字段是布爾類型,則加is)和字段名前加set的setter方法。這導(dǎo)致的一個(gè)問題是開發(fā)時(shí)需要編寫大量的getter方法和setter方法。為Java冗長的特點(diǎn)貢獻(xiàn)了一份力量。遵循這個(gè)規(guī)范很重要,以為在很多常用庫,比如Json序列化,會(huì)以getter方法作為字段存在的依據(jù)。
為了減少開發(fā)工作量,可以使用IDE自動(dòng)生成getter方法和setter方法。常見的Java IDE都支持自動(dòng)生成getter方法和setter方法。另一個(gè)方案是使用Lombok,通過Data,Getter,Setter等注解,讓編譯器在編譯時(shí)自動(dòng)生成getter方法和setter。
轉(zhuǎn)載于:https://www.cnblogs.com/skabyy/p/10049106.html
總結(jié)
以上是生活随笔為你收集整理的尝试Java,从入门到Kotlin(上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 歌词我的世界是什么歌啊
- 下一篇: asp.net Core多环境读取Jso