為什麼總是有人說Java囉嗦,卻沒人說C++囉嗦?

演算法與數學之美發表於2018-10-10

640?wx_fmt=jpeg


因為Java只支援物件導向一種程式設計正規化。

只支援一種正規化,好處是嚴謹,風格統一;壞處就是呆板、囉嗦。典型如它的main函式,再也不能像別的語言那樣寫成一個獨立的函式,而是必須包在一個沒有實際作用的類裡面……

這樣“不管有沒有必要,反正你必須把任何東西都先弄成類”的要求,顯然會導致許多本無必要的間接訪問以及由此衍生的其他很多麻煩——和其他語言相比,當然就顯得過於囉嗦了。

尤其是早期,相關理論尚未成熟,再加上單一正規化的限制,就使得它相對於早已成熟的其他語言,顯得表達能力過弱——有一個著名的吐槽,大意就是“之所以Java需要搞出那麼多設計模式,就是因為它表達能力太弱,別的語言很簡單就能做到的事,它就必須通過設計模式才能拐彎抹角的辦到”(當然,這些主要還是程式導向和麵向物件風格本身的區別,不能完全算是Java的缺陷)。

與之相反,C++是一種“多正規化”語言,非結構化、結構化、物件導向、巨集、模板……啥好用你就能得到啥——任何工作,你都有最簡化實現它的可能。但缺陷就是,再也沒人能“精通C++”——你可以拿它當C用,當Java用,當object-c用,玩模板黑魔法用……但最好別把它當C++用。不然真沒人hold得住它。

當年的Pascal也一樣:它僅支援結構化程式設計一種正規化,這種純粹而“板正”的風格使得它成了當時最佳的“教學語言”,但是很少被用到實際專案中。而當年的C,它支援結構化程式設計,同時也允許你任意解釋記憶體資料。這就使得它得到了額外的許多靈活性,甚至到現在都還充滿了生命力。

640?wx_fmt=jpeg


當然,過去和現在情況還不太一樣。

過去的編輯器沒現在這麼強大、智慧;Pascal的“板正”沒能帶來教學時概念清晰之外的更多好處,反而因為單一正規化的束縛讓人感覺礙手礙腳,自然不是C的對手。

但現代編輯器強大的語法提示功能給了程式設計師太大方便——“板正”的單一正規化強型別語言的語法提示實在太好做太強大了,你甚至很難把語法錯誤留到後半拉花括號敲出來之前:付出一點點“格式化”的代價,換來這麼大的好處……與之相反,過於靈活隨意的C++,它的語法提示可不是一般的難做。

別說200x年前後的“黑暗時代”,哪怕到現在,都還有不少主流編輯器沒辦法為C++程式碼提供Java級別的語法提示(我知道現在做的比較好的語法提示,其原理相當於在後臺邊編譯邊提示[實際是解析語法樹]:而且這個編譯器還必須支援“半拉函式”的動態編譯,不然很難給出準確的輸入建議)。

——亦因此,當年就沒人能勸我加入VIM教:在下寫C++必用eclipse/VS之類重量級IDE,不然語法提示真沒法看(當然,這都是上個年代的老黃曆了。現在已經有很多編輯器可以給出高水平的C++語法提示了,甚至可以自動提示模板例項化後的正確引數型別。至於VIM,它很早也已經可以配出基於語法的提示了;不過本人太懶,早年實驗幾次感覺效果不佳就再也沒用它寫過C++……)。類似的,由於編譯器發展水平所限,過去的Pascal語法“板正”,使得它需要付出很大的額外效能消耗(因為沒有辦法像C那樣讓程式設計師“抖機靈”);而現在的Java,卻因為自己的板正,給了位元組碼編譯器以及即地編譯器大量的提示,使得它們得以完成深度優化,從而得到了極佳的執行效率……

時代不一樣,配套技術成熟度不一樣,很多東西就不一樣了。總之,沒有免費的午餐,諸多相關也不是一成不變的;每一種成功語言之所以成功,都有其道理。評論利弊須就事論事,這才不容易翻船……

640?wx_fmt=jpeg


C++ 囉嗦,只是吐槽 C++ 囉嗦的人很多早就不在 JAVA 領域,做 Java 的也聽不到。

就囉嗦而言 C++ 和 Java 各有千秋。

為什麼吐槽 Java 的多,因為 Java 的 囉嗦很多時候影響到了業務實現。

如萬物皆物件,設個回撥、new 個執行緒、main 個函式,一個指標能搞定的問題非要扯上 class、 interface。滿頁的set /get 不煩嗎? 又比如多值返回,C++ 不支援,但用指標變相實現也很簡單,到了 Java 只能包個物件送回去。覺得有引用就夠了?寫個支援變數和函式繫結的逆波蘭試試。

Java 裡這類麻煩無處不在。這些繁瑣已經滲透到框架和庫的方方面面,躲都躲不掉。

加上很多參考的新語言出現,開發者也意識到這類語言設計特點帶來的麻煩其實是沒必要也沒意義的。

C++ 的 include、標頭檔案和衍生的 .d 這些用法都遠比 Java 的 import 囉嗦。

C++ 工具鏈的使用和配置都比 Java 麻煩花樣多。五花八門的編譯工具,dsl 還要加上 shell、python、lua 指令碼,一個個都比 ant maven 難學難用。

考慮跨平臺,還得了解各平臺各編譯器的引數、OS 環境變數、庫命名差異、路徑差異、巨集。

這些方面 Java 比 C++ 簡潔很多。

語言本身 C++ 不囉嗦嗎?hpp/cpp來回切,函式的前置宣告這些也很煩。

指標釋放,RTTI,這些 Java 開箱即用的到 C++ 都得造輪子,string,map 這些 stl 提供的很多人會重新造一遍。和 Netty 比 C++ 到現在網路程式設計這塊也沒個一統江湖的庫。

另外 C++ 開發者用第三方庫的態度和 Java 顯著不同,寫 C++ 的經常可以摳別人幾千行程式碼只為最後能少鏈一個外部依賴,平白多很多事(而且這種做法很多時候還是對的),寫 java 的可以只為用1個函式多加幾個鏈式依賴包也無所謂。

C++ 麻煩的部分像工具鏈、編譯、專案管理基本是一次性負擔,還都讓 SA 扛了。C++ 做業務因為 有指標、巨集和靈活到變態的模板,自己的業務程式碼抽象低寫的醜多半是自己的能力問題。複雜又通用的業務,要麼個體戶大神填了坑,要麼微軟們早封好了。每個業務域都有大量精短高效只能勉強看懂的程式碼和精短高效壓根全看不懂的程式碼,想吐槽下也很快就從“語言好爛”的憤慨變成“我好爛水好深”的感慨。加上還有遊戲引擎和編譯器這兩類魔王級專案,你說你業務複雜幾百萬程式碼 ,行摘兩段讓我們 diss 一下吧。

不是 C++ 不囉嗦,是 C++ 其他痛點和要操心的地方太多,囉嗦的那點事也就不顯眼了。加上語言靈活特性多,也容易靠優秀的個體發揮掩蓋這個缺陷。

並且 C ++ 已經是原教旨主義範疇的東西,和 VIM 一樣,很容易被懟“不是不好用,是你蠢不會用”,而且問題在於很可能還真是。

640?wx_fmt=jpeg


640?wx_fmt=jpeg


Java的囉嗦有兩個意思,1、在Java程式碼中存在很多重複,然後又沒辦法去掉,這裡暴露Java抽象能力不足的問題;2、Java的程式碼很嚴謹,介面使用什麼,很契合設計模式,經常是做一件事情,要沐浴更衣,要繁文縟節,要三請四請,九彎十八曲之後,才輪到正主兒正式登場演出。這是因為在Java是名詞的世界,而且型別嚴謹,所以才搞得這麼麻煩。這兩個原因糾纏在一塊,就搞得Java的程式碼沒法簡潔,在猿語中,Java是出了名的囉嗦。

C語言的抽象能力不如Java,但人家是弱型別,人肉型別管理,猿猴高興的話,隨便強制型別轉換,就直奔主題去了,Java的禮節規矩問題,在C猿中完全無法忍受,甚至,必要時還可以搞預處理,這可是節省程式碼的重要手段,雖然很醜陋,但是很管用。很多C開源庫的程式碼,很少體現型別的概念,實在也沒法體現,玩的是心驚肉跳驚心動魄。而C#對型別要求也很嚴謹,但是C#的抽象能力比java高好幾個層次,又配套了很多貼心語法糖,基本上可以替換C巨集的很多運用,用於精簡程式碼,非常有效。C#的語法設計,似乎骨子裡對程式碼囉嗦很排斥,所有導致囉嗦的地方,都會想心設法搞新語法糖去掉。

而大C++,抽象能力比C#還高一大截,而必要時,猿猴完全可以罔顧型別安全,C語言能Cast的,C++也能隨便Cast,const也可以Cast掉,而且,由於C++的型別推導能力很厲害,預處理在C++下又煥發出第二春。C++程式碼,如果猿猴高興,如果猿猴嘔心瀝血,基本上可以去掉程式碼中所有的重複。更何況,相比於C++本身的複雜,程式碼囉嗦只是微不足道的一個小問題。

640?wx_fmt=jpeg


640?wx_fmt=jpeg


Java就只支援一種正規化一種風格,不管你多會用都得羅嗦。而且Java社群無框架不開發的風氣加重了Java羅嗦的印象,畢竟按J2EE的風格你Hello個World就得上Spring折騰半天配置了人家C/C++幾行程式碼一句gcc完事呢。

C++抽象能力很強,有各種模板、TMP黑科技,惹急了還有巨集,只要你想搞你可以把C++寫得很簡潔,羅不羅嗦是可選的,至於實現黑科技過程中你寫的那一大堆template啊SFINAE什麼的,放進namespace detail裡對外人來說它們就不存在了,畢竟一大坨detail這能叫羅嗦麼,這是C++水平的證明

640?wx_fmt=jpeg


C++的語法更加複雜,只要靈活使用,確實可以大大簡化程式碼,例如:

  1. C++支援多繼承,可以方便的繼承多個類的方法。而Java實現類似的功能要使用介面,需要在每個子類實現每個介面方法,非常的囉嗦。

  2. C++支援操作符過載,可以實現很多複雜功能。在Java中,類似的功能需要用函式來實現,非常囉嗦。

  3. C++支援巨集定義,可以方便的實現非常複雜,方便,有用的程式碼模板(並不推薦),而Java沒有類似的功能。

不過,這幾個語法特性雖然可以簡化程式碼,但要謹慎使用。特別是巨集定義,因為難以除錯和維護,最好不要用來實現複雜的功能。

但是,Java也有很多方法來簡化程式碼。

  1. Java支援反射。使用反射,可以很容易的實現一些C++中實現起來很麻煩的功能。

  2. Java中,可以使用annotation和反射來自動生成程式碼,可以大大簡化程式設計。

∑編輯 | Gemini

來源 | 西部遊星

640?wx_fmt=png

演算法數學之美微信公眾號歡迎賜稿

稿件涉及數學、物理、演算法、計算機、程式設計等相關領域,經採用我們將奉上稿酬。

投稿郵箱:math_alg@163.com

相關文章