轉自:http://blog.163.com/gordonkkk/blog/static/63425684200962795039630/
從CSDN上看到一篇批評Java語言諸多問題的翻譯文章,原文作者是Mario Fusco。他指出了Java語言落後於時代,積重難返的10個問題。看過之後引起了我的一些聯想。下面列出他所說的10大問題。桔黃色字型是我一點點不成熟的思考。
1、缺少閉包(closure):我想這個不需要解釋了。函數式編程已經存在幾十年了,但最近幾年,它們獲得了越來越多的關注,最主要的原因,是它可以自然地編寫並行程式。我部分的同意Joshua Bloch強調在Java中引入閉包的問題需要再想一想(BGGA提議的方式真的很糟),至少閉包的缺失,使得在Java中做任何真正的函數式編程都是不可能的。
閉包的定義和特性,Java裡很難體會,因為Java裡沒有真正意義的閉包,我也是從JavaScript裡對閉包有了大致的瞭解。Java是個邏輯嚴格自洽的語言,嚴密到了一定程度,以至於目前的Java雷根本沒有閉包的容身之處——閉包中引用的外圍局部變數,到底放在堆棧中,還是託管堆中?他們的範圍和生存期如何處理?又該如何被GC回收?但閉包確實是好東西。即使我自認為是一個Java程式員,在寫多了JavaScript代碼又回到Java後,對於不能隨時隨地的寫一段{}括起來的代碼塊,感覺很不自由。針對閉包問題,Java還是有自己能將就的辦法,那就是內部類。至少還有內部類,至少內部類還有一部分的閉包特性,使得一些優雅的設計(如集合架構迭代器)能夠存在。
2、缺少一等函數:這個問題與前一個有些關聯,但我認為它更糟糕。在Java裡,要達到類似效果的唯一方式,是使用著名的、醜陋悲慘的單方法匿名內部類,但這看上去的確是一個拙劣的方法。甚至在C#中,也通過代理機制,提供了一個更好的實現。
所謂一等函數,物件導向的Java沒有似乎不能強求。但它帶來的問題,就是Java裡有時候策略模式的拙劣實現。C#作為一個進階語言,又是強型別的,也一樣可以有delegate,匿名方法,甚至Lambda運算式。Java裡就只能生造出一個單方法介面,new一個無意義的對象,來承載其中的函數。
3、原生類型(Primitive types):如果在Java中一切皆對象,那是多麼完美啊,但他們偏偏不這樣設計。因而,這一點導致了一些問題,比如,不能把一個int放到集合(Collection)裡,這個在Java5中通過自動裝箱特性得到瞭解決(下面會提到)。它也造成了傳值與傳引用上的困擾,原生類型資料是通過值傳給方法的(複製一份拷貝,然後傳給函數),而真正的對象是通過傳遞(譯註:其實是複製對象地址再傳遞,因此應該也是傳值方式,只是由於函數內部可通過這個對象地址訪問對象,因此效果上類似傳引用)。
這似乎不是個太大的問題,我覺得這裡有些吹毛求疵了。Effective Java裡建議在新的代碼中不要使用原生類型,這正是一個建設性的做法。Java並不是沒有原生類型的封裝類,不去使用的責任應該由程式員來負,而不是Java。
4、自動裝箱(Autoboxing)和自動拆箱(autounboxing):這個特性是為瞭解決因原生類型的存在所導致的問題,在Java5引入的。它允許靜默地轉換原生類型到相應的對象,但這常常導致其它的問題。比如Integer可以為null,但int不能,因此這時JVM只能拋出一個難以調試的null 指標異常(NullPointerException)。此外,它還可能導致其它奇怪的行為,就像下面的例子,我們就很難理解,變數test為什麼是false:
Intger a = new Integer(1024);
Intger b = new Integer(1024);
boolean test = a < b || a == b || a > b;
這個例子老實說我認為很不恰當。誠然,a和b都是對象,a<b和a>b的判斷中的自動拆箱和a==b的未拆箱造成了混淆,但要知道自動拆箱和裝箱本身只是便利措施,不能對它要求更多。這裡更有問題的其實是==判斷。String類遇到==也是一樣的問題,Java未在語言層級提供==的算符重載,個人認為是一種失策。
5、缺少範型具類化:範型是Java5引入的一個很酷的特徵,但是為了保持與舊版本Java的相容性,導致缺失某些重要的特性,尤其是不能在運行時反省範型的類型。例如,你有一個方法,接受List< ?>參數,如果傳進來一個List< String>,你卻不能知道運行裡該範型的確切類型。同理,你也不能建立範型數組。這意味著,儘管這樣的代碼看起來很自然,但卻不編譯不了。
擦除,意味著在JVM中所謂泛型,已經徹底還原成了基本的未參數化形式。Java的泛型,自我學習的第一天起,就不停的被告知,被提醒“擦除”的概念。擦除帶來的好處只有一個,就是和舊代碼相容。擦除帶來的不足卻有很多,而補償起來卻非常困難。同樣的,自從我學習Java泛型的第一天起,就不停的提醒我自己,你知道Java泛型能做什麼,現在更要知道它做不到什麼。有時候我們可以簡單的多引入一個參數記錄類型的Class對象,但更多的時候,享用泛型解決問題,此路不通。
6、不可避免的範型警告:你有發現過自己陷入不可能去掉的關於範型的警告嗎?如果你像我一樣大量使用範型,我打賭你碰到過。事實上,是這個問題的規模化癥狀,讓他們認為需要引入一個特定的註解 (@SuppressWarnings("unchecked")) 來處理這種情況,我覺得,範型應該可能被設計的更好。
泛型的轉換警告我感覺是很無厘頭的。從一個參數類型轉換到另一個,編輯器告訴我它可能是不安全的。為什麼是不安全的?因為它記不住型別參數,因為型別參數運行時要被擦除......既然自己無法做泛型層面的類型檢查,把維護型別安全的責任推給了程式員,又何必假模假式的做出警告?強制類型轉換的時候怎麼不來提醒轉換有可能失敗呢?兩者的區別又有多大呢?
7、不能傳void給方法調用:我得承認,這種給方法傳遞void的需求,乍一看有些怪異。我喜歡DSL,當我實現自己的DSL庫(lambdaj)的一個特定特性時,我不得不需要一個方法聲明成這樣的簽名:void doSomething(Object parameter),這裡為這個方法傳進來的參數parameter,是另一個方法調用的結果,它唯一的目的,是註冊調用(的對象)自身,以可以在以後執行它。讓我吃驚的是,即使println方法返回void,看上去也並沒有一個好理由,不允許我把代碼寫成這樣,:
doSomething(System.out.println("test"));
這條我基本沒看懂。我很同意他那句話“這種給方法傳遞void的需求,乍一看有些怪異”。如果是我的話,完全可以不設計這樣彆扭的API。void就是void,不應該再有更多的責任。想想JavaScript中Null和Undefined之間造成的混淆吧,Java裡就別來這套了。
8、沒有原生的代理機制:代理是一種非常有效和應用廣泛的模式,但Java提供的代理機制,只針對介面,而不是具體類。這是為什麼象cblib這樣提供這種機制的庫,被如此多的主流架構,如Spring和Hibernate,採用的原因。此外,由於cglib通過運行時建立被代理類的子類來實現的,因此這些種方式有一個眾所周知的限制——不能代理final類,比如String.
final類是用來做什麼的,不就是防止被改變的嗎?以它作為引用的類型,可以放心的認為對象的行為一定和類的定義中寫的一模一樣,這才是final的用意。從這個角度考慮,想要代理final類,與final的語義起了衝突。asm-cglib是很好的第三方工具,我認為足以滿足一般性的需求了。但如果Java能有一種能語言級的代理的支援,顯然會更好。無論以何種方式處理,總比運行時動態建立被代理的子類效率高,api的使用難度也會更低。
9、差勁的Switch……case語句:Java規定,switch……case只能選擇int和enum(Java5開始)。這一點如果跟更現代的語言如Scala相比,看起來簡直太弱了。
這個問題似乎在目前的Java中消失了.....很大的一個原因是,由於Java中Switch太爛,以至於很少使用了......
10、受檢查異常(Checked exception):類似原生類型,受檢查異常也已經成為Java的一個罪孽之源。它迫使程式員必須做下面兩件極其糟糕討厭的事情中的一個:讓你的代碼裡充斥大量的、糟糕難讀的、容易出錯的try……catch語句,而這樣做的最大意義,只是將捕獲的異常,封裝成運行時異常,然後再重新拋出;或者是讓大量的拋出聲明子句汙染你的API,讓介面缺少靈活性和可擴充性。
這是Java一個廣泛被批評的地方。我覺得Java的受檢查異常還是有意義的。在很多所謂一定會出異常,一定要考慮異常處理的地方,受檢查異常是合理的——你要麼應當立即處理它,要麼必須拋出它,真正必須處理的異常出現在介面中,並不算汙染API吧。現在的問題是,受檢查的異常被大量濫用。我個人認為,可以將這些異常化為運行時異常,並做一個小小的標記,在未被Catch或Throw時引起編譯器警告,再引入一個特定的,忽略該警告的註解。這樣可能會是一個圓滿的解決方案。
11、痛苦的線程同步編程:這是我自己認為Java的問題。Java的線程機制實在有點扯。你必須在腦中類比一切運行時的可能性,而任何微小的代碼變化都可能破壞你原先的設想。在這種情況下小心翼翼的線上程間通訊和同步。編寫安全執行緒的複雜代碼,簡直是一場頭腦風暴,並不是任何人都能勝任這樣的工作。這個問題和Java沒有閉包,不能進行函數式編程是緊密相關的,目前的Java很難做的更好,現有的API已經在盡量降低使用難度了。但涉及到實際業務的複雜需求時,Java不能提供任何語言層級的協助,還是讓人相當沮喪。當然事情發生在我這裡時,我只能說自己的水平差,不是一個足夠優秀的Java程式員。
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/jinxfei/archive/2009/09/20/4573317.aspx