Dart中的可选类型是如何工作的

Dart 语言是动态类型的。你可以编写、运行没有类型标注的任何程序,就像你使用Javascript的方式。

推荐专题: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 m = {
  2.     'one': new Partridge(),
  3.     'two': new TurtleDove(),
  4.     ...,
  5.     'twelve': new Drummer()};

我们本来也可以给m使用 Map ,但是那样的话,当我们获取内容的时候,它们将是Object的静态类型,而它只有很少的信息。因为map的内容除了Object外没有公共的super接口,我们可能更愿意使用 Dynamic 。如果我们像这样调用map 的值的方法:

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

如果内容是Object类型,我们会得到警告,因为Object不支持container方法。如果我们使用Dynamic类型,就不会产生警告。

范型

Dart 支持具体化范型(reified generics)。就是说,范型类型的对象在运行时携带它们的类型参数。传递类型参数给范型类型的构造函数是运行时操作。这如何与可选类型的要求相一致呢?

好吧,如果你不想总是考虑类型,范型并不强迫你。你可以创建范型类的实例,而不需要提供类型参数。例如,这样写没问题:

 
 
 
  1. new List();

当然,如果你想要,也可以这样写:

 
 
 
  1. new List();
  2. new List();

是下面这样的快捷方式:

 
 
 
  1. new List();

在构造函数中,类型参数起到运行时角色。实际上,它们在运行时被传递,所以你可以做动态类型测试的时候使用它们。

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

    Dart中的范型符合程序员的直觉。这是一些更有趣的情况:

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

    与此相反,类型标注(例如变量前添加的类型或者函数和方法的返回类型)起到非运行时角色并且不影响程序的语义。***一个值得学习的情况:

     
     
     
    1. new List() is List          // 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. [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中的可选类型是如何工作的
    本文地址:http://www.shufengxianlan.com/qtweb/news12/367812.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联