class ADuck
def quack()
puts "quack A";
end
end
class BDuck
def quack()
puts "quack B";
end
end
# quack_it doesn't care about the type of the argument duck, as long
# as it has a method called quack. Classes A and B have no
# inheritance relationship.
def quack_it(duck)
duck.quack
end
a = ADuck.new
b = BDuck.new
quack_it(a)
quack_it(b)
XMLPersistence xmlPersistence
= (XMLPersistence)persistenceManager.getPersistence();
xmlPersistence = persistence_manager.persistence.
Ruby的動態類型意義上不是弱類型——Ruby經常需要你傳遞正確類型的對象。事實上,Java強制類型轉換比Ruby要弱。例如,Java里面:”4”+2 等于”42”,這里會將整數轉化為字符串,在Ruby里會拋出一個TypeError,告訴你這個“can't convert Fixnum into String.”(Fixnum類型是不可以轉化為String的)。同樣的,Java里,因為作類型校正犧牲了速度,而且過多地做了整型操作,產生像Integer.MAX_VALUE + 1的整型,和Integer.MIN_VALUE等價,可是Ruby類型校正整型只是在需要的時候。
不論Ruby有什么優點,Java的靜態類型可以讓它在大規模的項目里面作為首選:Java工具能夠在開發時候明白代碼意思。IDE能夠在類之間依賴跟蹤,找到方法和類的用處,自動檢標識符而且幫助你檢測代碼。同樣的雖然Ruby工具在這些功能上存在限制,它缺乏類型信息所以不能夠完成上邊這些工作。
利用動態腳本編寫你的Java應用程序以及重用你的Java類庫
自從計算機誕生以來,軟件開發就傾向于使用高級語言進行開發。從匯編,到C,到C++,再到JAVA,每一次升級就會面臨來自各界同樣的問題:太慢、而且有太多的Bug、開發者不想放棄對這些原有語言的使用。漸漸地,隨著硬件的快速發展,新的研究和開發技術大大改進了編譯器、解釋器、和虛擬機,開發者不得不向高級語言轉移,放棄他們使用的低級語言開發以提高生產力(將他們從低級語言的障礙中釋放出來以提高他們的生產力)。
Java現在在軟件開發的很多領域里面占有主導地位,但是在這個發展過程中,動態腳本很有可能無情地取代它的地位。許多年以來,像Python、Perl、Rexx、Groovy、TCL和Ruby這樣的語言能夠在很多專業領域里面非常出色地工作,例如文件處理、自動測試、軟件構建、代碼重構、和Web圖形頁面設計——他們有著歷史性的名字“腳本語言”。而且在最近的一些年里,在大多數由Java,C++和其他編譯型計算機語言開發的大型工作里面,他們也取得了相應的進展。
去年的時候,Ruby on Rails(RoR)Web框架使Ruby有了更進一步的發展。RoR結構利用簡單的Ruby代碼定義了一個典型的多層次Web應用程序——圖形頁面層、業務邏輯層和數據持久層,因此減小了冗余文件、樣本文件代碼、生成的源代碼以及配置文件。RoR框架能夠更加優化更加容易地使用Ruby語言;而且Ruby,這種完善的腳本語言,相對于RoR框架來說可以在更多的領域里面使用。
作為一個長期的Java開發者,我很可能堅持在一段時間里一直用Java作開發。但是我仍然保持在我開發的基于Java的系統里面使用其他的語言,而且Ruby最近顯示出來是特別好的一種候選語言。在JRuby解釋器的幫助下,Ruby和Java一起工作得很好,包括配置、整合、和Java軟件的重用。而且在簡單學習Ruby的過程中也提高了我Java代碼的質量。使用Ruby可以讓我很容易地完成像功能程序和元程序一樣的技術手法,這些技術手法我在Java里面都是很難實現的。學習這些Ruby里面的技術手法可以幫助我更好鑒別什么時候而且怎樣在Java開發中使用它。
這篇文章,我希望能夠和你一起分享我在開發Java系統的時候使用Ruby的那種興奮感。我比較一下Java和Ruby的優點和缺點,而且介紹一下JRuby解釋器的支持者和反對者。而且我會向大家顯示區分Ruby和Java使用的最佳實踐以讓它們各自得到最優化的使用。我會使用一些簡單的代碼來舉例說明這個觀點,并且介紹一個消息實例來展示在Java系統里面怎樣結合使用Ruby,使其能夠更好地使用動態元程序語言的彈性、表現方式以及功能。
Ruby vs. Java
這篇文章從一個Java開發者的角度解釋了Ruby,主要是集中比較這兩種語言。像Java一樣,Ruby也是一種完全的面向對象的語言。但是這兩種語言有很大的不同。Ruby是動態類型的而且是在源代碼解釋器里面運行的,這種語言能夠像程序和功能范例一樣支持元編程。我這里不會介紹Ruby的具體語法,接下來的文章里面會廣泛地覆蓋其他各個方面。
動態類型
Java有靜態類型。你定義每個變量的類型,接下來在編譯的過程中,如果你使用了類型錯誤的變量將會得到一個編譯時錯誤。Ruby卻相反,擁有動態類型:你不用定義函數和變量的類型,而且沒有到運行的時候不會使用類型檢測,如果你調用一個不存在的方法就會得到錯誤信息。盡管這樣,Ruby不會關心一個對象類型,僅僅看它是否在一個方法里面調用了這個對象的方法。因為這個原因,這種動態方法可以得到這樣一個duck類型:“如果一個事物走起來像一只鴨子(duck)而且像一只鴨子(duck)呷呷地叫,它就是一只鴨子?!?BR>
Listing1.Duck typing
class ADuck
def quack()
puts "quack A";
end
end
class BDuck
def quack()
puts "quack B";
end
end
# quack_it doesn't care about the type of the argument duck, as long
# as it has a method called quack. Classes A and B have no
# inheritance relationship.
def quack_it(duck)
duck.quack
end
a = ADuck.new
b = BDuck.new
quack_it(a)
quack_it(b)
Java也可以通過反射讓你使用動態類型,但是這種笨拙冗長的工作會導致很多混亂的異常發生,像NoSuchMethodError和InvocationTargetException;在實踐中,這些異常傾向于在Java反射的代碼中突然出現,而且相對于Ruby而言出現頻率更高。
即使在沒有使用反射的Java代碼中,你會經常丟失掉靜態類型的信息。比如,在Command設計模式里面使用execute()方法必須返回Object勝于在Java代碼里面使用的特殊類型,結果會導致很多ClassCastException發生。同樣的,當在編譯時和運行時修改方法簽名的時候,運行時錯誤就會發生。在實踐開發中,不論是Java還是Ruby,這樣的錯誤很少引起嚴重的程序Bug。一個健壯的單元測試——任何時候你都會用到的——通常都能夠及時捕捉他們。
Ruby的動態類型意思是你不用重復問你自己一個問題:在Java里面你是否經常在一行里面遇到這樣冗長的代碼:
XMLPersistence xmlPersistence
= (XMLPersistence)persistenceManager.getPersistence();
Ruby消除了這種對于類型定義和轉換的需要,上邊的代碼用一個典型的Ruby等價表達為;
xmlPersistence = persistence_manager.persistence.
Ruby的動態類型意義上不是弱類型——Ruby經常需要你傳遞正確類型的對象。事實上,Java強制類型轉換比Ruby要弱。例如,Java里面:”4”+2 等于”42”,這里會將整數轉化為字符串,在Ruby里會拋出一個TypeError,告訴你這個“can't convert Fixnum into String.”(Fixnum類型是不可以轉化為String的)。同樣的,Java里,因為作類型校正犧牲了速度,而且過多地做了整型操作,產生像Integer.MAX_VALUE + 1的整型,和Integer.MIN_VALUE等價,可是Ruby類型校正整型只是在需要的時候。
不論Ruby有什么優點,Java的靜態類型可以讓它在大規模的項目里面作為首選:Java工具能夠在開發時候明白代碼意思。IDE能夠在類之間依賴跟蹤,找到方法和類的用處,自動檢標識符而且幫助你檢測代碼。同樣的雖然Ruby工具在這些功能上存在限制,它缺乏類型信息所以不能夠完成上邊這些工作。
JRuby:Java里的Ruby
作為一個Java程序員,你不要想在產品中使用Ruby直到你能夠讓它和存在的Java應用程序和類庫進行交互,而這些程序和類庫之中能夠支持Ruby的很多種類的基本功能。JRuby,JVM下的一個開源Ruby解釋器,能夠在Java里面使用Ruby類庫。就像標準的Ruby解釋器一樣,除開使用Ruby調用本地方法(C代碼)或者Java類庫以外,Ruby代碼都能夠在JRuby里面正確執行。
相比較于微軟的.NET平臺的公共語言運行時,JVM往往只能夠支持一種語言。但是事實上,JVM平臺不僅僅能夠支持Java,而且可以支持Python、JavaScript、Groovy、Scheme,和其他各種語言,這意味著有必要的時候,Ruby代碼能夠和這些語言很好地進行交互。
在2006年7月中旬,JRuby僅僅有一個預覽版本(0.9)。但是它迅速發展起來:一個志愿者團隊從2005年一月開始總共發布了五個版本。JRuby通過針對標準解釋器的不斷評估測試逐漸成熟起來,而且現在已經超過90%的測試都是在基本支持Ruby on Rails這個框架。
為了嘗試JRuby,保證Java SE 5 是安裝好了的而且JAVA_HOME環境變量也是設置好了的。從JRuby的工程頁面下載壓縮包然后解壓。設置JRUBY_HOME環境變量到JRuby安裝的根目錄。你可以在bin目錄里面嘗試著用jirb進行交互。大多數場合,你將使用JRuby解釋器——創建一個文件將文件名作為一個參數傳遞到JRuby的bin目錄下批處理腳本。
除了執行先前的Ruby代碼,你仍然可以使用JRuby來構造Java對象,調用Java方法,從一個Java類繼承。一個Ruby類能夠實現Java接口——有必要的話可以在Java里面靜態調用Ruby方法。
為了從Ruby訪問Java需要初始化類庫,需要以”java”命令開始。接下來用include_class方法指定需要使用的Java類,比如,include_class “javax.jms.Session”。你能夠使用include_package導入整個Java包到Ruby模塊里面。就像Java導入包的通配符語句一樣,盡量避免include_package使用產生的名稱沖突是明智的;在JRuby里,如果解釋器為了需要的類搜索所有的包也是格外不明智的。盡可能嚴格地使用include_class。
很多Java標準類的名稱和Ruby類的名稱相同。為了解決這樣的沖突,傳遞一個代碼塊到include_class函數,為這個Java類返回一個新名稱,而且JRuby將使用這個名稱作為Java類的別名。(見Listing4)
Listing 4. Include a Java class with clashing name
require "java"
# The next line exposes Java's String as JString
include_class("java.lang.String") { |pkg, name| "J" + name }
s = JString.new("f")
或者,你可以創建一個包含java類定義的Ruby模塊,但是需要在一個隔離的名稱空間里面。例如:
Listing 5. Java module importing multiple Java classes
require "java"
module JavaLang
include_package "java.lang"
ends = JavaLang::String.new("a")
JRuby的好處是什么?
像Ruby一樣的動態語言經常使用在專業領域就像整合其他系統一樣;JRuby在Java里面扮演了這個角色。比如,JRuby能夠從一個系統讀出數據,將這個數據傳遞插入到另外一個系統里。當需求改變的時候,修改一段JRuby腳本相對于修改配置文件來說簡單得多,所以避免了Java綜合代碼里面復雜的編譯和發布周期。
除開在Ruby里使用Java,你也可以從在Java里使用Ruby,讓你的應用程序容易編寫。利用JRuby的最小化構造語句方法,你可以創建更加容易使用的專業領域語言供用戶工作。比如,一個賭博引擎的腳本系統能夠引入Ruby類來描述字符,媒介和其他游戲實體。
此為,使用Ruby的動態機制,用戶能夠改變腳本類的定義。這些Ruby對象允許直接使用方法管理它的狀態和行為。另外一方面,一般使用用戶配置好的鍵值通過Java映射傳遞,削弱了對象的功能完整性。
Ruby腳本有點像加速過的配置文件。一般Java應用程序的配置都是使用xml文件或者屬性文件,但是這些對于參數定義在開發時間上受到了一定限制。使用Ruby腳本能夠使你的系統要么從一個文本要么從一個內置編輯器里面讀取,用戶能夠自由自定義行為無論什么情況只要你想放置腳本。這樣的方法,Ruby使用行為結合配置,提供了Java插件API的功能,而且不需要JavaIDE或者編譯器,節省了構建和發布jar文件的步驟。
例如,一個用戶提供的腳本能夠嵌入到一個應用程序事件管理里面用來對確認的可疑條件進行過濾,然后將發送一個通知給系統管理員以及在一個特定的安全發行數據庫里面記入日志,或者啟動腳本能夠清除舊文件以及支配用戶數據存儲。同樣的很多富客戶端允許用戶改變菜單和工具條的位置——使用Ruby嵌入,一個用戶的新菜單可以觸發任何用戶想要的行為。
為了方便編寫Java應用程序,Bean Scripting Framework(BSF)在JVM和多種動態語言之間提供了一種標準接口,包括Ruby、Python、BeanShell、Groovy、和JavaScript;Java Specification Request(JSR)223,提供BSF的成功規范,將成為Java 6里面的標準部分。Java代碼能夠發送一個變量到JRuby的名稱空間JRuby可以直接操作這些Java對象或者返回一個值到Java。使用BSF和JSR223,在Java和任何腳本語言之間的用于解釋的語法是相同的。Listing 6顯示了一個BSF使用Ruby的基本例子,這些在線例子的完整代碼放置在bsf_example目錄下。注意BSF不僅包括包外的JRuby支持;但是增加它的簡單指令在JRuby文檔里面是可使用的。
Listing 6. Embed a Ruby interpreter
...
// JRuby must be registered in BSF.
// jruby.jar and bsf.jar must be on classpath.
BSFManager.registerScriptingEngine("ruby",
"org.jruby.javasupport.bsf.JRubyEngine", new String[]{"rb"});
BSFManager manager = new BSFManager();
// Make the variable myUrl available from Ruby.
manager.declareBean("myUrl", new URL("http://www/jruby.org"), URL.class);
// Note that the Method getDefaultPort is available from Ruby
// as getDefaultPort and also as defaultPort.
// The following line illustrates the combination of Ruby syntax
// and a Java method call.
String result = (String) manager.eval(
"ruby", "(java)", 1, 1, "if $myUrl.defaultPort< 1024 then " +
"'System port' else 'User port' end");
在Java里面直接使用JRuby解釋器也是可能的,但是像這樣會連接你的Java代碼到Ruby規范化的Java封裝類,這些在BSF/JSR223里面是很嚴格的。
局限性
很重要的一點是你必須記住JRuby仍然在開發中,它還有很多局限,這些在1.0 release版本之前會固定下來。
一個Ruby類是不能從一個Java抽象類繼承的。不幸的是,這種限制使你不能簡單創建一個具體的Java子類,使用虛方法實現抽象方法用于讓Ruby類來繼承。這是因為在Ruby/Java繼承在JRuby的最近的版本中的第二個限制:Java代碼不能多態地調用重寫一個Java方法的Ruby方法。
這些限制讓Swing的使用顯得很困難。比如,通過繼承AbstractTableModel去利用添加到TableModel接口的功能是不可能的。你能夠將繼承轉化為代理繞過這個限制:使用一個具體Java填充類繼承于抽象類作為類型接口的一個代理,這個接口包括所有的Java方法。一個Ruby類實現這個代理接口。盡管這種做法近似于排除了JRuby的平常優勢,但是它提供了基于這個局限的工作區而且在需要的地方提供了彈性:在子類功能里。
Ruby的標準庫提供了很多方面的功能。這些不使用本地代碼的方式,在JRuby版本中是包含了的。JRuby團隊逐漸地完善很多類庫,雖然一些是在本地代碼上的一個簡單層次,就像GUI類Tk一樣,將不會再完善。包含其中的Java標準庫,提供了很多必要的功能。
JRuby現在在WEBrick Web服務器上提供了Ruby on Rails的基本支持;接下來的一個milestone版本里面將會在任何Servlet容器里面實現RoR的支持。RoR支持將使程序員利用一系列存在的Java庫與Web框架的簡易性結合在一起,但是確認JRuby是一種可選的Ruby解釋器。
因為有BUG,JRuby 0.9需要Java SE 5,但是將會在下一個版本中支持JRE 1.4。
例子
例子jms_example.rb舉例說明了JRuby增長長度的用法。它顯示了如何將消息從一個類結構傳到另外一個類,結合兩種不同點的模擬軟件定義了分布式游戲引擎。這些代碼使用先進的功能和元程序技術將任何類型的XML消息作為Ruby對象進行傳輸。代碼注釋可以幫助理解程序邏輯。
在Java里面使用配置到達這種水平是不可能的;至少,用戶能夠插入編譯代碼。但是在Ruby里,動態代碼是像靜態代碼一樣自然和容易使用的。
Ruby和Java的未來
Ruby可以教會Java程序員很多。RoR框架顯示了開發Web應用程序是如此的簡單;利用JRuby,RoR將能夠重用存在的Java功能。JRuby將在Java應用程序中加入Jython,JavaScript,和其他各種動態腳本語言。想要掌握這些技巧的開發人員需要學習動態腳本,這些將會覆蓋越來越多的應用程序開發領域。盡管不使用動態腳本,Java開發者也能夠看到Ruby帶來的利益如同功能程序和元程序的概念。
原文轉自:http://www.anti-gravitydesign.com