作者PsMonkey (痞子军团团长)
看板Ajax
标题彻底了解 GWT Part 2:JS 的 overlay type
时间Fri Feb 12 23:03:55 2010
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