歡迎您光臨本站 註冊首頁

Dart中的可選類型是如何工作的

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  

 Dart 語言是動態類型的。你可以編寫、運行沒有類型標註的任何程序,就像你使用Javascript的方式。

51CTO推薦專題:Google Dart新結構化編程語言

你可以在程序中添加類型標註:

◆ 添加類型不會阻止你程序的編譯和運行——即使標註不完整或錯誤。

◆ 不論你添加了什麼類型標註,你的程序都具有完全相同的語義。

然而,添加類型標註可以使你獲益。類型提供了下面這些好處:

◆ 給人看的文檔。明智地放置類型標註可以使別人更容易地閱讀你的代碼。

◆ 給機器看的文檔。工具可以有多種方式利用類型標註。特別是,它們可以在 IDE 中幫助提供很好的特性,如名稱補全和增強的導航。

◆ 早期的錯誤檢測。Dart 提供了靜態檢查器,它可以警告你潛在的問題,而不用你自己查。另外,在開發模式中,Dart 自動把類型標註轉換為運行時斷言檢查來輔助調試。

◆ 有時,在編譯到 Javascript 時,類型可以幫助改進性能。

靜態檢查器

靜態檢查器(static checker)行為很像C中的鏈接。它在編譯時警告你潛在的問題。這些警告中的很多是和類型相關的。靜態檢查器不會產生錯誤——不論檢查器說什麼你總是可以編譯和運行你的代碼。

檢查器不會對每個可能的類型違反都敏感。它不是類型檢查器(typechecker),因為Dart並不是按照典型的類型系統那樣使用類型。檢查器會抱怨那些非常可能是真實問題的地方,而不會強迫你去滿足心胸狹隘的類型系統。

例如,考慮這個:

  1. String s1 = '9';  
  2. String s2 = '1';  
  3. ...  
  4. int n = s1 + s2;  
  5. print(n); 

這裡明顯是個問題。這種情況下靜態檢查器會產生一個警告。注意代碼依然可以運行,n 被置為字元串'91'並列印出來。

然而,不像典型的強類型系統,這樣的代碼:

  1. Object lookup(String key) {...} // a lookup method in a heterogenous table  
  2. String s = lookup('Frankenstein'); 

檢查器不會抱怨。因為這種情況下代碼很有可能是對的,雖然缺少類型信息。你作為程序員通常知道程序的語義,而類型檢查器(typechecker)不知道。你知道'Frankenstein'這個key在表中存儲的是字元串,即使 lookup 方法聲明返回的是Object。

Dynamic類型

沒有提供類型的時候,Dart如何避免抱怨呢?這其中的關鍵就是 Dynamic 類型,這是程序員沒有明確給出類型時候的默認類型。使用 Dynamic 類型讓檢查器閉嘴。

偶爾,你可能想要明確地使用 Dynamic 。

  1. Map<String, Dynamic> m = {  
  2.     'one': new Partridge(),  
  3.     'two': new TurtleDove(),  
  4.     ...,  
  5.     'twelve': new Drummer()}; 

我們本來也可以給m使用 Map

  1. pearTree = m['one'].container(); 

如果內容是Object類型,我們會得到警告,因為Object不支持container方法。如果我們使用Dynamic類型,就不會產生警告。

范型

Dart 支持具體化范型(reified generics)。就是說,范型類型的對象在運行時攜帶它們的類型參數。傳遞類型參數給范型類型的構造函數是運行時操作。這如何與可選類型的要求相一致呢?

好吧,如果你不想總是考慮類型,范型並不強迫你。你可以創建范型類的實例,而不需要提供類型參數。例如,這樣寫沒問題:

  1. new List(); 

當然,如果你想要,也可以這樣寫:

  1. new List<String>();  
  2. new List(); 

是下面這樣的快捷方式:

  1. new List<Dynamic>(); 

在構造函數中,類型參數起到運行時角色。實際上,它們在運行時被傳遞,所以你可以做動態類型測試的時候使用它們。

  1. new List<String>() is List<Object>  // true: every string is an object   
  2. new List<Object>() is List<String>  // false: not all objects are strings 

Dart中的范型符合程序員的直覺。這是一些更有趣的情況:

  1. new List<String>() is List<int>     // false  
  2. new List<String>() is List          // true  
  3. new List<String>() is List<Dynamic> // same as line above  
  4. new List() is List<Dynamic>         // true, these are exactly the same  

與此相反,類型標註(例如變數前添加的類型或者函數和方法的返回類型)起到非運行時角色並且不影響程序的語義。最後一個值得學習的情況:

  1. new List() is List<String>          // true as well! 

你可以不用類型寫程序,但是你經常要傳遞數據到有類型的庫中。為了防止類型妨礙你,沒有類型參數的范型類型被認為是任何其它范型類型的替代品(子類型)。

檢查模式

在開發過程中,Dart 程序可以在檢查模式(checked mode)下運行。如果你在檢查模式下運行程序,在參數傳遞、返回結果和執行賦值時,系統將自動執行某些類型的檢查。如果檢查失敗,程序將在該處停止執行,並帶有清晰的錯誤信息。所以,

  1. String s = new Object(); 

將會停止執行,因為Object不是String的子類型。然而,

  1. Object foo(){return "x";}  
  2. String s = foo(); 

工作正常,因為foo在運行時返回的實際對象就是String,儘管其類型簽名說foo返回的是Object。當對象賦值給變數時,Dart 檢查對象的運行時類型是否為變數(靜態)聲明類型的子類型。

本質上,檢查模式就像是在對每次賦值、返回等進行子類型檢查的調試器下運行。一些更複雜的例子:

  1. <int>[0,1, 1][2] = new Object(); // fails in checked mode  
  2.    
  3. bar(int n) { return n *2;}  
  4. ...  
  5. bar(3.2); // returns 6.4 in production, but fails in checked mode 

在檢查模式下,每次把參數傳遞給函數時,都要檢查參數的運行時類型是否是形式參數聲明類型的子類型。我們可以很容易地糾正這個:

  1. bar(num n) { return n *2;}  
  2. ...  
  3. bar(3.2); // works fine  
  4.    
  5. int i_bar(num n) { return n *2;}  
  6. ...  
  7. i_bar(3.2); // fails in checked mode  
  8.             // because returned value is not an int 

注意最後一行。檢查發生在返回值上,即使函數的結果並沒有進行賦值。

讓我們回到之前的Frankenstein例子上。

  1. Object lookup(String key) {...} // a lookup method in a heterogenous table  
  2. String s = lookup('Frankenstein'); 

如果我們假設的lookup方法返回一個String是正確的,那麼檢查模式會平滑地執行。如果不是,那麼它將捕獲到我們的這個錯誤。在生產模式(production mode)下,代碼都會運行,不會抱怨。假設lookup方法真的返回了一個非String對象,一個Frankenstein類的實例。那麼變數 s 將容納那個實例。Dart 絕不會神奇地強制它為一個字元串。如果Dart那樣做就會意味著類型標註正在改變我們程序的行為,類型就不再是可選的了。

當然,如果你根本就不用類型,檢查模式不會妨礙你。

  1. my_add(s1, s2) { return s1 + s2;}  
  2.    
  3. my_add(3, 4); // 7  
  4. my_add("3", "4"); // "34" 

所有這些檢查會帶來很大的性能損失,所以通常不能用在生產環境中。這些檢查的好處是它們可以在源頭上捕獲動態類型的錯誤,更容易地調試問題。雖然總可以在測試過程中發現大多數這類問題,但是檢查模式有利於縮小它們的範圍。

使用類型

如何使用類型取決於你。如果你討厭類型,你不必使用它們。你不會得到任何類型的警告,你可以用你在其它動態語言中感到舒適的方式開發。然而你依然可以從類型中獲益,因為Dart的庫中有類型簽名,它們告訴你它們期望什麼和返回什麼。如果你在檢查模式中運行,傳遞了錯誤的參數給類庫,檢查模式將在你犯錯的地方發現它們。

如果你喜歡類型,你可以在任何地方使用它們,很像是靜態類型語言。然而,即使那樣你也不會獲得同樣級別的靜態檢查。Dart的規則比較寬鬆。我們期望為這些人提供額外的工具來更加嚴格地解釋類型標註。

我們不建議太極端地使用方式。應該在有意義的地方使用類型。你能做的最有價值的事情是添加類型到你類庫中公有成員的聲明上。接下來,再對私有成員做同樣的事。即使沒有別人需要維護代碼,如果你離開代碼幾周或幾個月後又回來,你會發現它是有幫助的。在這兩種情況下,你不一定要在方法體或函數體中添加類型。庫的使用者從類型簽名中獲得價值,即使它們不是100%準確。

在函數體中,並不總是需要標註聲明。有時代碼足夠簡單,真的無所謂,類型反而可能會造成混亂。

通常,你應該設計代碼,別讓考慮類型影響你。在某些情況下,有幾種替代的設計,其中的某種比其它更適合使用類型。例如,你可以用傳遞函數替代它,而不是用傳遞字元串表示要調用的函數名,這樣代碼會更有效並更容易檢查類型。Dart 同樣防止以其他方式無端地使用反射(reflection)。然而,當真正有意義時,你應該毫不猶豫地使用反射。

原文:http://han.guokai.blog.163.com/blog/static/136718271201110194459405/

 



[火星人 ] Dart中的可選類型是如何工作的已經有455次圍觀

http://coctec.com/docs/program/show-post-71453.html