Java程序員,你為什么要關注Scala
原創51CTO編輯推薦:Scala編程語言專題
【51CTO獨家特稿】上周我們從Scala創始人Martin Odersky的訪談錄中了解了Scala創建的背景,這次讓我們來看一看Martin Odersky對于Scala語言的設計目標是怎么說的。在創造“超越Java的語言”的過程中,具體都需要考慮到哪些方面呢?
讓步
Frank Sommers:您之前提到,想要創造一種存在于Java體系內,集成Java基礎架構的語言。為了做到這一點,Scala要做出什么樣的讓步,使其能夠兼容Java平臺?
Martin Odersky:很幸運,我們不需要做出太多妥協,或者說,很難判斷我們所做出的所有妥協都是對我們不利或是有利的。其中一個我們不得不做的妥協是購買Java的靜態重載模型。也許我們應該更積極地嘗試一些其他方法,如使用多方法,盡管當時我們嘗試過這一點,但并沒有充分探討有關多方法的設計。也許直到今天也還沒有充分探討過,所以我不能完全肯定這種方法是否行得通。這種方法本來有著令人振奮的可能性,但我們并沒有采用它,因為我們想保持與Java的兼容性。
另外一件時而會引來人們否定的事是Scala既包含Traits技術又包含類技術。大家認為一個整潔的設計應該是只采用Traits技術。目前已有一些只采用Traits而放棄類概念的整潔設計,但我們并沒有這樣做。因為我們想從這兩方面保留與Java的互用性。我們希望有一種方式可以讓Java代碼很容易地調用Scala代碼,而Traits并不含有映射到Java的特性,因為在Java中根本不存在這種技術。因此,我們選擇了Java中所擁有的類的概念,因為我們希望能夠向后映射,這樣我們就可以很容易地在兩個方向保留互用性。
第3個問題,與其說是語言上的問題,不如說是類庫的問題。我們很想要拋棄null(空值)這種概念。NULL是很多錯誤的根源。在Scala中,不允許null作為任何類型的可能值,取而代之的是選項類型(option type)。當然,有很多Java類庫中的類都返回null,我們必須要解決這個問題。
51CTO編者注:在新語言兼容舊語言的這個方面,《Think in Java》和《Think in C++》的作者Bruce Eckel也曾經寫過一篇文章,就C++和C的關系評論兼容老語言為新語言帶來的好處和限制。這篇文章相關的討論可以參見這里。
#p#
選擇你需要的戰斗
Bill Venners:為了能讓Java程序員接受Scala,您都做了哪些努力?例如,像Java使用大括號而不是其它符號來分塊程序,這使得C和C + +程序員感到使用Java就如同使用C和C++一樣。
Martin Odersky:我們并沒有為了推銷產品而設計什么特殊功能,但是為了避免產生使用障礙,我們做了一些努力。舉例來說,我認為大括號起了很好的分割作用,所以我選擇使用它。我們本可以堅持使用begin/end或是其它符號,但我不認為這些符號能起到更好的效果。
其中有一個我們做了改動的地方。最初我們曾使用冒號-等號表示變量賦值,就像是Pascal、Modular和Ada那樣,使用單個等號表示相等。很多編程理論會認為這是最恰當的方式。賦值并不表示相等,因此,你必須使用不同的符號。但后來我與一些使用Java的人進行交流。我得到的反應是,“哦,這看起來像是一個很有趣的語言。但是,為什么你要寫冒號-等號?它是什么意思?”我解釋說,意思就像Pascal中使用的那樣。他們說,“那我明白了,但我不明白為什么你堅持要使用這個。”然后我意識到,這并不是我們想要堅持的東西。我們并不想說,“我們有了一個更好的語言,因為我們用冒號-等號代替了等號。”這完全是不值一提的小事,人們完全可以適應任何方式。因此,我們決定不再計較這些小事,而是有其他更重要的地方我們想要與眾不同。
Bill Venners:這次您沒有說那句您曾經說過的“選擇你需要的戰斗”。基本上,您認為,等號并不那么重要,還有其他您更在意的東西。那么那些重要的事情是什么?您有什么方法能夠說服人們去改變他們的想法或程序?
Martin Odersky:我們首要關心的一件事是,擁有一個盡可能整潔的集成了函數式和面向對象的程序設計。我們希望擁有一流的功能,還希望擁有其他函數式程序設計的特征,如類型,泛型,模式匹配。我們希望能夠以一個更加整潔的方式整合函數式和面向對象。這是我們從一開始就深感關心的事情。
后來,我們發現這實際上是很容易實現的,因為函數式語言有一套固定的特點。這些特點已經被深入研究和充分證明過了,因此,我們所面臨的問題只是如何以最佳方式整合這些特點到面向對象程序設計。在做Pizza時,我們已經進行了嘗試,在Scala,我認為我們得到了兩者之間更順暢的集成。但后來我們發現,在面向對象方面仍有很多事情有待開發。面向對象程序設計,至少擺在一個靜態類型系統上,是一個非常未知領域。目前我們可以看到和使用一些已有的工作,但我們發現幾乎所有的語言都做出了很大的妥協。
因此,隨著開發Scala,我們開始發現如何能夠混合對象,如何能夠抽象自我類型,如何能夠使用抽象類型成員,以及如何能夠讓這一切集成到一起。已有一些研究語言以特殊方法關注了以上幾個方面,但幾乎還沒有任何主流語言,能夠涵蓋以上所有方面。最終結果表明,Scala的主要創新在于面向對象方面,這也是我們真正關心的事情。
#p#
面向對象的創新
51CTO推薦閱讀:面向對象的思維過程
Bill Venners:您能不能給出一個具體的列表,列出一些您認為是Scala面向對象的創新?
Martin Odersky:首先,我們想要創造一個純粹的面向對象的語言,其中的每個值都是一個對象,每個操作都是一個方法調用,每個變量都是一些類的成員。因此,我們不想要靜態變量,但我們需要一些其他的東西來替代它們,所以我們提出了Singleton對象。但是,即使是Singleton對象仍然是全局結構。因此,我們所面臨的挑戰是如何盡可能少地使用它們,因為當你使用一個全局結構時,你就不能再改變它了。你不能實例化它。它很難測試。很難對它以任何方式進行修改。
因此,我們所面臨的挑戰是,如何能夠不使用靜態或全局思想來建立復雜的組件。尤其是,我們不得不處理組件間的遞歸依賴。例如我有兩個組件A和B。A使用B,同時B使用A。我要如何才能讓他們發現對方,并協同工作?我們所做的第一件事是基于mixin(混合式)合成的概念,然而,Java只有單一類和一串含有虛定義而沒有代碼的接口概念,Scala有類和Traits概念,其中Traits可以包含帶有定義和函數域的方法,例如可以帶有函數體。然后,我們就擁有了mixin合成,而不只是具有類實現接口,在mixin合成中,我們定義類和所有Traits。有關這項工作是如何工作的細節在此就不詳述,我們稱之為線性化。
因此,我們定義一個線性化Scala。但隨之而來的問題是,怎么樣能讓一個mixin發現其他mixin?如果他們需要一些來自其他mixin的服務,他們如何指定目標?標準的面向對象方式是通過抽象成員,只要你處理方法,這種方式就會運作良好,但是,我們還必須要處理變量。如何才能讓mixin找到對方的域?更重要的是,我們不得不處理類型。因為我們從一開始就有類型嵌套,就像內部類,這是另一個我認為非常重要的事情。如果你選擇了mixin合成,一個Trait如何能夠找到有關所有其他Trait的內部類,然后訪問那些類?我們發現(不僅是發現,更是設計),我們可以通過在自我類型上定義一個抽象來做到這一點。
那么“自我類型上的抽象”究竟是什么意思?比如在一個類中,this類型代表什么意思?你會說,“表示這個類的類型。”大多數人都會這么回答。但實際上卻沒有任何令人信服的理由。This類型很可能是指其他的東西。只存在一個界定條件,就是當你實際創建一個類的實例的時候,然后這個你所創建的對象就與該類有同樣的功能。這就與處理抽象方法時相同。你可以在一個類中定義一個抽象方法,并且不需要實現。這其實并不危險,因為當你創建一個該類的實例時,你將會檢查是否所有抽象方法都有具體實現。因此,自我類型的抽象只是這種問題的一個一般化,也可以讓你處理域和類型。
自我類型抽象的技術非常值得探索。早期,我們建立一種演算來研究這個問題,稱為nu對象演算—νObj,我們于2003年,在面向對象程序設計歐洲會議上公布它( ECOOP 2003年)。我們最初發現這一概念只是作為一個技術訣竅,為了使演算過程能夠簡單。直到后來開始使用它之后我們發現,也許這將是有益于語言的東西。那時候我們還不太了解到底會有什么用處,但我們決定嘗試。直到后來,我們才發現,它實現了我們想要的,即讓一個Traits聲明它所需要的來自其他Traits的東西。這就像是現在的一些工具,如Spring所實現的功能,被稱為依賴注入。但通常這種依賴注入只能適用于域,也許對方法也管用,但我們可以讓它還能應用于類型。此外,在Scala中,這種注入是靜態完成而不是運行時完成,其次,它達到了對于內部類型的類型安全要求。因此,從某種意義上說,它比目前的一些工具如Spring做的更好。
我為什么要學習Scala?有什么好處嗎?
51CTO推薦閱讀:基于JVM的語言正在開始流行
Bill Venners:以后的采訪中,我們將會繼續再次探討依賴注入,您剛才談到關于自我類型的“抽象”。您提到的必須要解決的兩件事是函數式和面向對象的融合以及面向對象的創新。如果我的工作室Java編程,在實際工作中,這些東西如何能夠幫助我?我能得到什么實質的益處?
Martin Odersky:我們正面臨的挑戰之一,是我們想融合函數式和面向對象。我們很早就確定了這種概念,即不可變類是非常非常重要的。現在大家都在談論不可變類,因為人們認為這是解決目前由多核電腦所引起的問題的一個關鍵方法。大家都說,不管你做什么,你都要嘗試讓你的代碼盡可能多的使用不可變類。在Scala中,我們也從很早就采用這種方法。五,六年前,我們開始深刻考慮不可變的類。但事實證明,大量的面向對象領域一直都在使用可變對象。對于他們來說,可變狀態和對象是同一回事:可變狀態是一個對象中必不可少的組成部分。我們必須從本質上解釋這個問題。
例如,對于一個標準的Java對象,你將創建一個域,基本上是一個可變域。然后你還需考慮構造函數,接收參數并為域賦值。這就是可變的概念,內置到每一個Java類。現在,Java具有final域概念,它不被認為是可變的,因為它只在構造函數中被賦值一次。但你仍然能看到賦值操作。我們希望能有一個更清晰的概念,讓你不需要看到構造函數和賦值操作。
使用Scala我們最后所做的事是直接確定類的參數。你只需要在類名后寫一個參數列表,這些就成為類的參數。不存在獨立可變域和構造函數的概念,這些實際上轉變成了一些我們必須要解決的問題。其中之一是,如果你想要有幾個構造函數,那要怎么辦?我們必須為其確定語法和規則。你可以在你的primary(主要)構造函數之外使用auxiliary(輔助)構造函數。另一個問題是,如果你希望你的參數作為一個域可見化,該怎么辦?需要創建一個單獨的域,然后再賦值嗎?或者是有可能傳遞參數給一個類,然后立即變成一個域而對其他人可用?因此,我們必須為其創建語法,我相信這種做法也是全新的。最后在面向對象方面還有很多其他新穎的想法。
Bill Venners:對于我這樣的一個Java程序員,能得到什么益處?
Martin Odersky:好處就是你可以用更簡潔的語法來定義類。使用Scala編寫類,能夠更容易、更簡潔。如果你想要使用不可變類,那將更加容易,因為它非常適合做這件事。你還可以像使用Java那樣來使用Scala定義可變類。這甚至比用Java更方便,但Scala真正耀眼的地方還在于它的不可變類。它比Java更自然、更簡明。
編者后記:
隨著業界開始流傳基于JVM的語言正在開始流行這樣的聲音,處在基于JVM的非Java語言之首的Groovy和Scala被寄以相當大的期望。曾經有一篇英文文章對于幾個JVM語言(Groovy,Scala和JRuby)進行了比對,結果是無論從速度,緩存需求還是垃圾處理方面,Scala都是最適合企業級開發的語言。究竟Scala是否真的如此神奇,還讓我們拭目以待。
【相關閱讀】