看板 Ajax 關於我們 聯絡資訊
GWT 一直處在一個很尷尬的角色 我一直不知道該放在 Java 版還是在 Ajax 版討論 之前都是在介紹 or 教學,跟 JavaScript 還沒啥關係 所以就都放在 Java 版 這篇因為談到了 GWT 處理 JavaScript object 的思維 所以就貼在這裡 或許對於 JavaScript 感到厭煩的卻又不得不寫的人 這是一條生路.... [炸] 如果想了解 GWT 的版友,可以到 http://pt2club.blogspot.com/search/label/GWT 有幾篇粗劣的入門文章可以稍微略知一二 =============================================================== 網頁版: http://pt2club.blogspot.com/2010/02/gwt-part-2javascript-overlay-type.html ==========================[正文開始]=========================== 原文:http://googlewebtoolkit.blogspot.com/ 2008/08/getting-to-really-know-gwt-part-2.html 技術校正、審閱:tkcn 假設你已經在 GWT module 當中,愉快地使用 JSNI 來呼叫某些手寫的 JavaScript。一切運作正常,但是 JSNI 只能在獨立的 method 下運作。某些整 合性狀況需要你徹底地把 JavaScript 跟 Java 的 object 綁在一起——寫 DOM 跟 JSON 就是兩個好例子——所以我們十分需要可以從 Java 程式碼直接與 JavaScript object 互動的方法。換句話說,我們想要 JavaScript 的 object 看起來就像我們寫的 Java object。 GWT 1.5 引入了 JavaScript overlay type,這讓 GWT 程式整合各種 JavaScript object 變得容易許多。這個技術有很多好處,像是讓你能用 Java IDE 的 code completion 跟 refactoring 功能,即使你寫的是 untype 的 JavaScript object。 範例:簡單、有效率的 JSON 用一個範例來瞭解 overlay type 是最簡單的方法。假設我們要存取一組「 customer」數據,底層是用 JSON object。在 JavaScript 中的資料結構可能像 這樣: void jsonData = [ { "FirstName" : "Ps", "LastName" : "Monkey" }, { "FirstName" : "痞子", "LastName" : "猴" }, { "FirstName" : "Pt2", "LastName" : "Club" }, { "FirstName" : "STO", "LastName" : "Orz" }, ]; 要把一個 Java type 加到上述的資料結構,要從建立一個 JavaScriptObject 的 subclass 開始,這在 GWT 表示是一個 JavaScript 的 object。接著增加一 些 getter。 // An overlay type class Customer extends JavaScriptObject { // Overlay types always have protected, zero-arg ctors protected Customer() { } // Typically, methods on overlay types are JSNI public final native String getFirstName() /*-{ return this.FirstName; }-*/ public final native String getLastName() /*-{ return this.LastName; }-*/ // Note, though, that methods aren't required to be JSNI public final String getFullName() { return getFirstName() + " " + getLastName(); } } 如此一來,GWT 就會瞭解所有 Customer 的 instance 實際上是來自 GWT module 以外的 JavaScript object。這包含了很多意義。舉例來說,看到 getFirstName() 跟 getLastName() 裡頭的 this reference。它實質上是代表 一個 JavaScript object,所以你操作這個 this 就像在 JavaScript 裡頭一樣 。在這個例子中,我們可以直接存取 JSON 中那些我們已知的 field: this.FirstName 跟 this.LastName。 那麼,你要如何才能真正得到一個被包裝成 Java type 的 JavaScript object 呢?你不能用 new Customer() 來建構它,因為重點是把一個既有的 JavaScript object 包裝成 Java type。因此,我們必須使用 JSNI 來得到這樣 一個 object: class MyModuleEntryPoint implements EntryPoint { public void onModuleLoad() { Customer c = getFirstCustomer(); // Yay! Now I have a JS object that appears to be a Customer Window.alert("Hello, " + c.getFirstName()); } // Use JSNI to grab the JSON object we care about // The JSON object gets its Java type implicitly // based on the method's return type private native Customer getFirstCustomer() /*-{ // Get a reference to the first customer in the JSON array from earlier return $wnd.jsonData[0]; }-*/; } 現在來搞清楚我們做了啥。我們拿到了一個 plain old JSON object(譯註:源 自於 POJO)並且建立一個看起來很正常的 Java type,讓 GWT 程式碼中能夠使 用它。於是你就有了 code completion、refactoring、compile 階段的檢查—— 這些寫 Java 時所擁有的好處。然而,你還是可以靈活地操作任何 JavaScript object,這使得存取 JSON service(使用 RequestBuilder)變得很輕而易舉。 為一些 compiler 強者岔題一下。overlay type 另一個美妙的事情是你可以增 加 Java 的 type,但是卻不用影響底層的 JavaScript object。注意到上面例 子中,我們加入的 getFullName() 這個 method。它是純粹的 Java 程式碼(並 不存在於底層的 JavaScript object),但卻是依照底層 JavaScript object 所寫的。也就是說,處理同一個 JavaScript object,以 Java 的角度會比用 JavaScript 功能豐富得多;而且不用動到底層的 JavaScript object——無論 是 instance 或是 prototype。 (接續上一段的題外話)在 overlay type 增加 method 這個很酷的怪招是可行 的,因為 overlay type 的設計規則是不允許 polymorphic 呼叫,所有的 method 必須是 final 且/或 private。因此,compiler 是靜態地解讀每一個 overlay type 的 method,所以不需要在 runtime 的時候動態 dispatch。這是 為甚麼我們不用拘泥在 object 的 function pointer;compiler 可以直接對 method 呼叫,就好像是 global function、獨立於 object 之外。很明顯的, 直接呼叫 function 會比間接快得多。更棒的是,因為呼叫 overlay type 的 method 是靜態解讀的,這些動作會嘗試自動 inline;這在為了 script 語言的 效率而奮戰時,是非常強大的火力支援。接下來我們會重新來一遍,展示給你看 這個方法有多成功。 範例:lightweight collection 我們在上面的例子當中掩蓋了某些事情。getFirstCustomer() 這個 method 是 非常不切實際的。你一定會希望存取全部的 customer 陣列。所以,我們需要一 個 overlay type 來表示這個 JavaScript 陣列。幸運的是,這很簡單: //泛型在 overlay type 裡頭也運作正常! class JsArray<E extends JavaScriptObject> extends JavaScriptObject { protected JsArray() { } public final native int length() /*-{ return this.length; }-*/; public final native E get(int i) /*-{ return this[i]; }-*/; } 現在我們可以寫出更有趣的程式了: class MyModuleEntryPoint implements EntryPoint { public void onModuleLoad() { JsArray<Customer> cs = getCustomers(); for (int i = 0, n = cs.length(); i < n; ++i) { Window.alert("Hello, " + cs.get(i).getFullName()); } } // Return the whole JSON array, as is private final native JsArray<Customer> getCustomers() /*-{ return $wnd.jsonData; }-*/; } 這是一個很乾淨的程式碼,尤其是以建立靈活配置的角度來看。正如上頭提到的 ,compiler 可以作一些十分 fancy 的事情,讓它相當有效率。看一下 onModuleLoad() 這個 method 在沒有 obfuscate 的 compile 結果: function $onModuleLoad(){ var cs, i, n; cs = $wnd.jsonData; for (i = 0, n = cs.length; i < n; ++i) { $wnd.alert('Hello, ' + (cs[i].FirstName + ' ' + cs[i].LastName)); } } 這個最佳化真的是 xx 的好。即使是 getFullName() 這個 method 的 overhead 也沒了。事實上, 所有 Java method 的呼叫動作都不見了。當我們說:「GWT 給你可負擔的 abstraction」,這就是其中之一。不僅 inline 的程式碼執行速 度明顯變快、我們不再需要定義 function 的內容、也因而得以將 script 簡短 化(雖然持平而論,inline 的方式也很容易讓 script 量變多,所以我們小心 地在速度與程式碼大小之間取得平衡)。現在回顧上頭原始的 Java 程式碼是十 分有趣的,而試著推導 compiler 最佳化的步驟就展示到這邊。不過,我們還是 忍不住要 show 一下對應、obfuscate 過的程式碼: function B(){var a,b,c;a=$wnd.jsonData;for(b=0,c=a.length;b<c; ++b){ $wnd.alert(l+(a[b].FirstName+m+a[b].LastName))}} 注意在這個版本當中,唯一沒有 obfuscate 的是 JavaScript 當中的識別字, 例如 FirstName、 LastName、jsonData 等。這是為甚麼即使 GWT 努力讓大量 JavaScript 交互溝通的操作變得容易,但我們還是努力說服別人盡量用純 Java 來寫程式、而不是混著 JavaScript 寫。希望你聽到我們講這些之後,你會明白 我們不是要打擊 JavaScript——只是我們不能對它們最佳化,這會讓我們很沮 喪。 摻在一起作撒尿牛丸 overlay type 是 GWT 1.5 的重要特性。這個技術讓直接與 JavaScript library 互相溝通變得相當容易。希望在讀完這篇文章之後,你可以想像如何以 一組 Java type 直接導入任何 JavaScript library 到 GWT 裡頭,進而使用 Java IDE 來進行高效率的開發跟 debug,卻不會因為任何類型的 GWT overhead 而影響程式碼大小或執行速度。同時,overlay type 作為一個強大的 abstraction 工具,提供更優雅的低階 API,例如新的 GWT DOM package 。 -- 錢鍾書: 說出來的話 http://www.psmonkey.org 比不上不說出來的話 Java 版 cookcomic 版 只影射著說不出來的話 and more...... -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 61.228.193.140