新聞中心
Dart 語言是動態(tài)類型的。你可以編寫、運(yùn)行沒有類型標(biāo)注的任何程序,就像你使用Javascript的方式。

推薦專題:Google Dart新結(jié)構(gòu)化編程語言
你可以在程序中添加類型標(biāo)注:
◆ 添加類型不會阻止你程序的編譯和運(yùn)行——即使標(biāo)注不完整或錯(cuò)誤。
◆ 不論你添加了什么類型標(biāo)注,你的程序都具有完全相同的語義。
然而,添加類型標(biāo)注可以使你獲益。類型提供了下面這些好處:
◆ 給人看的文檔。明智地放置類型標(biāo)注可以使別人更容易地閱讀你的代碼。
◆ 給機(jī)器看的文檔。工具可以有多種方式利用類型標(biāo)注。特別是,它們可以在 IDE 中幫助提供很好的特性,如名稱補(bǔ)全和增強(qiáng)的導(dǎo)航。
◆ 早期的錯(cuò)誤檢測。Dart 提供了靜態(tài)檢查器,它可以警告你潛在的問題,而不用你自己查。另外,在開發(fā)模式中,Dart 自動把類型標(biāo)注轉(zhuǎn)換為運(yùn)行時(shí)斷言檢查來輔助調(diào)試。
◆ 有時(shí),在編譯到 Javascript 時(shí),類型可以幫助改進(jìn)性能。
靜態(tài)檢查器
靜態(tài)檢查器(static checker)行為很像C中的鏈接。它在編譯時(shí)警告你潛在的問題。這些警告中的很多是和類型相關(guān)的。靜態(tài)檢查器不會產(chǎn)生錯(cuò)誤——不論檢查器說什么你總是可以編譯和運(yùn)行你的代碼。
檢查器不會對每個(gè)可能的類型違反都敏感。它不是類型檢查器(typechecker),因?yàn)镈art并不是按照典型的類型系統(tǒng)那樣使用類型。檢查器會抱怨那些非??赡苁钦鎸?shí)問題的地方,而不會強(qiáng)迫你去滿足心胸狹隘的類型系統(tǒng)。
例如,考慮這個(gè):
- String s1 = '9';
- String s2 = '1';
- ...
- int n = s1 + s2;
- print(n);
這里明顯是個(gè)問題。這種情況下靜態(tài)檢查器會產(chǎn)生一個(gè)警告。注意代碼依然可以運(yùn)行,n 被置為字符串'91'并打印出來。
然而,不像典型的強(qiáng)類型系統(tǒng),這樣的代碼:
- Object lookup(String key) {...} // a lookup method in a heterogenous table
- String s = lookup('Frankenstein');
檢查器不會抱怨。因?yàn)檫@種情況下代碼很有可能是對的,雖然缺少類型信息。你作為程序員通常知道程序的語義,而類型檢查器(typechecker)不知道。你知道'Frankenstein'這個(gè)key在表中存儲的是字符串,即使 lookup 方法聲明返回的是Object。
Dynamic類型
沒有提供類型的時(shí)候,Dart如何避免抱怨呢?這其中的關(guān)鍵就是 Dynamic 類型,這是程序員沒有明確給出類型時(shí)候的默認(rèn)類型。使用 Dynamic 類型讓檢查器閉嘴。
偶爾,你可能想要明確地使用 Dynamic 。
- Map
m = { - 'one': new Partridge(),
- 'two': new TurtleDove(),
- ...,
- 'twelve': new Drummer()};
我們本來也可以給m使用 Map
- pearTree = m['one'].container();
如果內(nèi)容是Object類型,我們會得到警告,因?yàn)镺bject不支持container方法。如果我們使用Dynamic類型,就不會產(chǎn)生警告。
范型
Dart 支持具體化范型(reified generics)。就是說,范型類型的對象在運(yùn)行時(shí)攜帶它們的類型參數(shù)。傳遞類型參數(shù)給范型類型的構(gòu)造函數(shù)是運(yùn)行時(shí)操作。這如何與可選類型的要求相一致呢?
好吧,如果你不想總是考慮類型,范型并不強(qiáng)迫你。你可以創(chuàng)建范型類的實(shí)例,而不需要提供類型參數(shù)。例如,這樣寫沒問題:
- new List();
當(dāng)然,如果你想要,也可以這樣寫:
- new List
(); - new List();
是下面這樣的快捷方式:
- new List
();
在構(gòu)造函數(shù)中,類型參數(shù)起到運(yùn)行時(shí)角色。實(shí)際上,它們在運(yùn)行時(shí)被傳遞,所以你可以做動態(tài)類型測試的時(shí)候使用它們。
- new List
() is List - new List
Dart中的范型符合程序員的直覺。這是一些更有趣的情況:
- new List
() is List // false - new List
() is List // true - new List
() is List // same as line above - new List() is List
// true, these are exactly the same
與此相反,類型標(biāo)注(例如變量前添加的類型或者函數(shù)和方法的返回類型)起到非運(yùn)行時(shí)角色并且不影響程序的語義。***一個(gè)值得學(xué)習(xí)的情況:
- new List() is List
// true as well!
你可以不用類型寫程序,但是你經(jīng)常要傳遞數(shù)據(jù)到有類型的庫中。為了防止類型妨礙你,沒有類型參數(shù)的范型類型被認(rèn)為是任何其它范型類型的替代品(子類型)。
檢查模式
在開發(fā)過程中,Dart 程序可以在檢查模式(checked mode)下運(yùn)行。如果你在檢查模式下運(yùn)行程序,在參數(shù)傳遞、返回結(jié)果和執(zhí)行賦值時(shí),系統(tǒng)將自動執(zhí)行某些類型的檢查。如果檢查失敗,程序?qū)⒃谠撎幫V箞?zhí)行,并帶有清晰的錯(cuò)誤信息。所以,
- String s = new Object();
將會停止執(zhí)行,因?yàn)镺bject不是String的子類型。然而,
- Object foo(){return "x";}
- String s = foo();
工作正常,因?yàn)閒oo在運(yùn)行時(shí)返回的實(shí)際對象就是String,盡管其類型簽名說foo返回的是Object。當(dāng)對象賦值給變量時(shí),Dart 檢查對象的運(yùn)行時(shí)類型是否為變量(靜態(tài))聲明類型的子類型。
本質(zhì)上,檢查模式就像是在對每次賦值、返回等進(jìn)行子類型檢查的調(diào)試器下運(yùn)行。一些更復(fù)雜的例子:
[0,1, 1][2] = new Object(); // fails in checked mode - bar(int n) { return n *2;}
- ...
- bar(3.2); // returns 6.4 in production, but fails in checked mode
在檢查模式下,每次把參數(shù)傳遞給函數(shù)時(shí),都要檢查參數(shù)的運(yùn)行時(shí)類型是否是形式參數(shù)聲明類型的子類型。我們可以很容易地糾正這個(gè):
- bar(num n) { return n *2;}
- ...
- bar(3.2); // works fine
- int i_bar(num n) { return n *2;}
- ...
- i_bar(3.2); // fails in checked mode
- // because returned value is not an int
注意***一行。檢查發(fā)生在返回值上,即使函數(shù)的結(jié)果并沒有進(jìn)行賦值。
讓我們回到之前的Frankenstein例子上。
- Object lookup(String key) {...} // a lookup method in a heterogenous table
- String s = lookup('Frankenstein');
如果我們假設(shè)的lookup方法返回一個(gè)String是正確的,那么檢查模式會平滑地執(zhí)行。如果不是,那么它將捕獲到我們的這個(gè)錯(cuò)誤。在生產(chǎn)模式(production mode)下,代碼都會運(yùn)行,不會抱怨。假設(shè)lookup方法真的返回了一個(gè)非String對象,一個(gè)Frankenstein類的實(shí)例。那么變量 s 將容納那個(gè)實(shí)例。Dart 絕不會神奇地強(qiáng)制它為一個(gè)字符串。如果Dart那樣做就會意味著類型標(biāo)注正在改變我們程序的行為,類型就不再是可選的了。
當(dāng)然,如果你根本就不用類型,檢查模式不會妨礙你。
- my_add(s1, s2) { return s1 + s2;}
- my_add(3, 4); // 7
- my_add("3", "4"); // "34"
所有這些檢查會帶來很大的性能損失,所以通常不能用在生產(chǎn)環(huán)境中。這些檢查的好處是它們可以在源頭上捕獲動態(tài)類型的錯(cuò)誤,更容易地調(diào)試問題。雖然總可以在測試過程中發(fā)現(xiàn)大多數(shù)這類問題,但是檢查模式有利于縮小它們的范圍。
使用類型
如何使用類型取決于你。如果你討厭類型,你不必使用它們。你不會得到任何類型的警告,你可以用你在其它動態(tài)語言中感到舒適的方式開發(fā)。然而你依然可以從類型中獲益,因?yàn)镈art的庫中有類型簽名,它們告訴你它們期望什么和返回什么。如果你在檢查模式中運(yùn)行,傳遞了錯(cuò)誤的參數(shù)給類庫,檢查模式將在你犯錯(cuò)的地方發(fā)現(xiàn)它們。
如果你喜歡類型,你可以在任何地方使用它們,很像是靜態(tài)類型語言。然而,即使那樣你也不會獲得同樣級別的靜態(tài)檢查。Dart的規(guī)則比較寬松。我們期望為這些人提供額外的工具來更加嚴(yán)格地解釋類型標(biāo)注。
我們不建議太極端地使用方式。應(yīng)該在有意義的地方使用類型。你能做的最有價(jià)值的事情是添加類型到你類庫中公有成員的聲明上。接下來,再對私有成員做同樣的事。即使沒有別人需要維護(hù)代碼,如果你離開代碼幾周或幾個(gè)月后又回來,你會發(fā)現(xiàn)它是有幫助的。在這兩種情況下,你不一定要在方法體或函數(shù)體中添加類型。庫的使用者從類型簽名中獲得價(jià)值,即使它們不是100%準(zhǔn)確。
在函數(shù)體中,并不總是需要標(biāo)注聲明。有時(shí)代碼足夠簡單,真的無所謂,類型反而可能會造成混亂。
通常,你應(yīng)該設(shè)計(jì)代碼,別讓考慮類型影響你。在某些情況下,有幾種替代的設(shè)計(jì),其中的某種比其它更適合使用類型。例如,你可以用傳遞函數(shù)替代它,而不是用傳遞字符串表示要調(diào)用的函數(shù)名,這樣代碼會更有效并更容易檢查類型。Dart 同樣防止以其他方式無端地使用反射(reflection)。然而,當(dāng)真正有意義時(shí),你應(yīng)該毫不猶豫地使用反射。
原文:http://han.guokai.blog.163.com/blog/static/136718271201110194459405/
分享文章:Dart中的可選類型是如何工作的
網(wǎng)頁URL:http://m.5511xx.com/article/dhjshpc.html


咨詢
建站咨詢
