美文网首页dart
Dart语法系列(三):函数与异常

Dart语法系列(三):函数与异常

作者: 不喝咖啡的程序员 | 来源:发表于2019-03-07 15:26 被阅读0次

一、 Functions (函数)

Dart 是一个真正的面向对象语言,函数也是一个对象并且具有其类型:Function。这意味着,方法可以赋值给变量,也可以当做其他方法的参数。您还可以像调用函数一样调用Dart类的实例。详情请参考 Callable classes

Dart是不支持函数的重载的****,一个类中不允许存在同名的函数出现,这点与C语言一样。虽然不支持函数重载但是它支持可选参数的使用

下面是定义方法的示例:

bool isNoble(int atomicNumber) {
  return atomicNumber != null;
}

Dart中函数的返回值类型 和 参数类型都可以省略****,但是Effective Dart 中推荐最好不要省略

isNoble(atomicNumber) {
  return atomicNumber != null;
}

对于只包含一个表达式的函数,可以使用简写语法:(return必须省略)

bool isNoble(int atomicNumber) => atomicNumber != null; //return必须省略

//同样可以省略类型,下面等价
isNoble(atomicNumber) => atomicNumber != null;

这个 => expr 语法是 { return expr; } 形式的缩写。=> 形式 有时候也称之为 箭头语法( arrow syntax)

注意: 在箭头 => 和冒号 ; 之间只能使用一个表达式(expression) ,而不能使用语句(statement)。 例如:你不能使用 if statement,但是可以 使用条件表达式 conditional expression

方法可以有两种类型的参数:必需的和可选的。命名可选参数也可以标记为@required

1.1、可选参数

可选参数可以是基于命名参数或者基于位置的参数,但是这两种参数不能同时当做可选参数。

如果参数列表中同时存在可选参数 和 必选参数,必选参数必需放在可选参数前面

Optional named parameters(可选命名参数)

在定义方法的时候,使用 {param1, param2, …} 的形式来指定命名可选参数。

在调用方法的时候,你可以使用这种形式 paramName: value 来指定命名参数,而不能直接传值:

//函数定义,注意有{} 
enableFlags({bool bold, bool hidden}) {
  // ...
}

//函数调用
enableFlags(hidden: false,bold: true);//OK 
enableFlags(bold: true);//OK
enableFlags();//OK
//enableFlags(false, true);//编译错误 

{}表示可选参数,当然你也可以按需传入可选参数,但是必选参数必选要传递

//a是必选参数, {}中的是可选参数
//void enableFlags({bool bold, bool hidden},int a) //错误定义,可选参数只能放在后面
void enableFlags(int a,{bool bold, bool hidden}) { 
  print(bold.toString()+" "+hidden.toString());
}

main() {
  enableFlags(3,bold: true); //true null
  enableFlags(3);  //null null
  //enableFlags(); 错误。有必选参数a存在,不能不传任何参数
}

注意,可选参数只能放参数列表的在最后

Optional positional parameters(可选位置参数)

把一些方法的参数放到 [ ] 中就变成可选 位置参数了,可选位置参数只能根据位置选择传入的参数,而不能通过命名的方式,其他的效果与可选命名参数一致:

//[]表示可选位置参数
void enableFlags(int a,[bool hidden, bool bold]) {
  print(bold.toString()+" "+hidden.toString());
}

main() {
  enableFlags(3,true,true); //true true
  enableFlags(3,true); //null true

}

Default parameter values(默认参数值)

在定义方法的时候,可以使用 = 来定义可选参数的默认值,只能用于可选参数(两种可选参数[ ]和{}都支持)。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null (Dart中万物都是对象,没有基本类型之分)

void enableFlags(int a ,{bool hidden = true, bool bold }) {
  print(bold.toString()+" "+hidden.toString());
}


main() {
  enableFlags(3); //null true
  enableFlags(3,hidden: false); //null false
  enableFlags(3,hidden: false,bold: false); //false false
}

版本问题: 旧版本代码可能需要使用一个冒号 (:) 而不是 = 来设置参数默认值。 原因在于 Dart SDK 1.21 之前的版本,命名参数只支持 :。 : 设置命名默认参数值在以后版本中将不能使用, 所以我们推荐你 使用 = 来设置默认值, 并 指定 Dart SDK 版本为 1.21 或者更高的版本。

还可以使用 list 或者 map 作为默认值。 下面的示例定义了一个方法 doStuff(), 参数类型分别为list和map,并分别为其指定了默认值,由于默认值必须是编译时常量,所以必须使用const修饰

void doStuff(
    {List<int> list = const [1, 2, 3], Map<String, String> gifts = const {
        'first': 'paper',
        'second': 'cotton',
        'third': 'leather'
      }}) 
{
  print('list:  $list');
  print('gifts: $gifts');
}


main() {
 doStuff();
}

1.2、main函数

每个应用都需要有个顶级的 main() 入口方法才能执行。 main() 方法的返回值为 void 并且有个可选的 List<String> 参数

下面是一个 web 应用的 main() 方法:

void main() {
  querySelector("#sample_text_id")
    ..text = "Click me!"
    ..onClick.listen(reverseText);
}

下面是一个命令行应用的 main() 方法,并且使用了方法参数作为输入参数:

// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
  print(arguments);

  assert(arguments.length == 2);
  assert(int.parse(arguments[0]) == 1);
  assert(arguments[1] == 'test');
}

1.3、函数是一等公民(Functions as first-class objects)

函数可以作为参数来进行调用,例如:

printElement(element) {
  print(element);
}

main() {
  var list = [1, 2, 3];
  list.forEach(printElement);  
}

输出结果为:1、2、3。 我们可以看看forEach的函数定义,直接声明一个函数的声明作为函数的参数

img

我们也可以为变量分配一个函数,例如下面:(这个例子使用一个匿名函数。下一节将详细介绍这些内容)

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

1.4、Anonymous functions(匿名函数)

你有可以创建没有函数名的函数,称之为匿名函数,有时候也被称为 lambda 或者 closure 闭包。 你可以把匿名方法赋值给一个变量, 然后你可以使用这个方法,比如添加到集合或者从集合中删除。您可以为变量分配一个匿名函数,例如,然后你可以使用这个方法,比如添加到集合或者从集合中删除。

匿名函数看起来类似于命名函数—有0个或者多个参数,在括号之间用逗号和可选类型标注分隔。后面的代码块包含函数的主体:

([[Type] param1[, …]]) { 
  codeBlock; 
}; 

下面的示例定义了一个无类型参数item的匿名函数。list 中的每个元素都会调用这个函数来 打印出来,同时来计算了每个元素在 list 中的索引位置:

var list = ['apples', 'bananas', 'oranges'];
//1、匿名函数允许直接赋予给一个变量
var a =  (item) {
  print('${list.indexOf(item)}: $item');
};
list.forEach(a);//a指向一个匿名函数

//2、也允许直接传入一个匿名函数,与上面的两句效果是等价的
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

/*打印结果
0: apples
1: bananas
2: oranges
*/

如果函数只包含一个语句,可以使用箭头语法缩写

var list = ['apples', 'bananas', 'oranges'];
  
var a = (item) => print('${list.indexOf(item)}: $item');
list.forEach(a);

//与上面两句等价
list.forEach((item) => print('${list.indexOf(item)}: $item'));

1.5、Lexical scope(静态作用域)

Lexical Scope (also called Static Scope)。Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。 基本上大括号里面定义的变量就只能在大括号里面访问,和 Java 作用域类似。

这里有一个嵌套函数的例子,每个作用域级别上都有变量:

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

nestedFunction() 可以访问所有的变量, 包含顶级变量。

1.6、Lexical closures(词法闭包)

一个 闭包(匿名函数) 是一个函数对象不管该对象在何处被调用, 该对象都可以访问其作用域内的变量

在下面的示例中,makeAdder()捕获变量addBy。无论返回的函数到哪里,它都会记住addBy:

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;  //匿名函数作为Function类型的对象进行返回
}

void main() {
    // Create a function that adds 2.
    var add2 = makeAdder(2);

    // Create a function that adds 4.
    var add4 = makeAdder(4);

    assert(add2(3) == 5);
    assert(add4(3) == 7);
}

在上面的示例中我们可以看出:普通函数,匿名函数都是一个Function类型的对象,并且可以作为返回值进行返回。

例如下面例子,add2指向的就是test函数对象,当执行add2()的时候实际上调用的是test()函数:

test(){
  print("Hello World");
}
 
Function makeAdder(addBy) {//返回类型可以省略
  return test ;
}

main() {
  var add2 = makeAdder(2);
  add2(); //Hello World 
}

1.7、判断函数相等

下面是测试顶级方法静态函数实例函数 相等的示例:(下面都是执行通过的)

foo() {}               // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {}        // An instance method
}

main() {
  var x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = new A(); // Instance #1 of A
  var w = new A(); // Instance #2 of A
  var y = w;
  x = w.baz;

  // These closures refer to the same instance (#2),
  // so they're equal.
  assert(y.baz == x);

  // These closures refer to different instances,
  // so they're unequal.
  assert(v.baz != w.baz);
}

上面的例子可以看出 同一个类的不同实例之间,对于同一个方法来说其对象是不相等的,既var v = A()var w = A() 中 v.bar 与 w.bar 方法对象是不相等的。

1.8、Return values(返回值)

所有函数都返回一个值。如果没有指定返回值,则 默认把语句 return null,作为函数的最后一个语句执行

foo() {}

assert(foo() == null);

二、异常

代码中可以出现异常和捕获异常。异常表示一些 未知的错误情况。如果异常没有捕获, 则异常会抛出,导致 抛出异常的代码终止执行。

和 Java 不同的是所有的 Dart 异常是非检查异常 unchecked exceptions(既不需要一定要用try ...catch处理)方法不声明它们可能抛出哪些异常,也不要求您捕获任何异常:

test(){//方法体也不用声明
  throw new FormatException("Test one ");
}

void main() {
    test();//不需要try-catch
}

Dart 提供了 ExceptionError 类型, 以及一些子类型。你还 可以定义自己的异常类型。但是, Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。( Dart抛出的异常类型不一定要是Exception和Error类型,任何非空类型都可以)。

2.1、throw

下面是抛出一个异常的示例:

throw new FormatException('Expected at least 1 section');//FormatException是Exception的子类

你也可以抛出任意对象:

throw 'Out of llamas!';

注意:在正式使用中通常抛出的是实现了Error或Exception类型的对象;

由于抛出异常是一个表达式,所以可以在 => 语句中使用,也可以在其他能使用表达式的地方抛出异常:

void distanceTo(Point other) => throw UnimplementedError();

2.2、Catch

与java一样,Dart中可以使用 try-catch来捕获异常,避免异常继续传递(除非你重新抛出rethrow异常)。不过与java不同的事,使用on ExceptionType来声明捕获具体的异常类型。on ExceptionType catch(e) 通过catch可以捕获异常对象e。如果没有指定异常类型,直接使用catch(e),则表示可以捕获任何异常类型。

只捕获OutOfLlamasException异常:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}

对于可以抛出多种类型异常的代码,你可以指定 多个捕获语句:

void main() {
  try {
   // throw new FormatException("Hello"); 只触发第一个on FormatException 
    
   // throw new TimeoutException("hello");只触发第二个on Exception catch (e)
  
      throw "Hello EeveyOne"; //只触发第三个catch (e)
      
  } on FormatException {
    //捕获具体的异常类型FormatException,没有异常对象
    print("I'm a OutOfLlamasException");
  } on Exception catch (e) {
    //只要是Exception类型的异常都能捕获,并携带异常对象e
    print('Unknown exception: $e');  
  } catch (e) {
    // 任何throw抛出的类型都能捕获
    print('Something really unknown: $e');
  }

}

如上面代码所示,你可以使用on 或者 catch 来声明捕获语句,也可以 同时使用使用 on 来指定异常类型,使用 catch 来 捕获异常对象

函数 catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 (一个 StackTrace 对象)。

void main() {
  try {
      throw "Hello EeveyOne";  
  }  on Exception catch (e) {
    print('Unknown exception: $e');
  } catch (e,s) { //e为异常对象,s为堆栈信息
    print('Exception details:\n $e');
    print('Stack trace:\n $s');
  }
}
img

使用 rethrow 关键字可以 把捕获的异常给 重新抛出,当然也可以使用 throw抛出异常,不过使用rethrow更方便:

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // Runtime error
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
    // throw  e;等价于上面
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

输出:

misbehave() partially handled NoSuchMethodError.
main() finished handling NoSuchMethodError.

2.3、Finally

要确保某些代码执行,不管有没有出现异常都需要执行,可以使用 一个 finally 语句来实现。<u>如果没有 catch 语句来捕获异常, 则在执行完 finally 语句后, 异常被抛出了</u>:

void main() {
  try {
      throw "Hello EeveyOne";
  } finally{
      //finally执行了,但是异常并没有被捕获,执行后立马抛出异常
    print("My Name Finally");
  }
    
   print("End");//没有而被执行
}

打印如下:

img

finally子句在所有匹配到的catch子句之后运行:

void main() {
  try {
      throw "Hello EeveyOne";
  } catch (e){
    print("Exception is $e");
  } finally{
    print("My Name Finally");
  }
  print("End");
}

输出结果为:

Exception is Hello EeveyOne
My Name Finally
End

想了解更多请阅读[Exceptions]章节。

欢迎关注我的公众号【不喝咖啡的程序员】,最新的文章会在上面发布:


image

相关文章

  • dart语法7-泛型

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法8-库

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法10-生成器

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法9-异步

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法11

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法2-内置类型

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法1

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法3-函数

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法4-操作符

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

  • dart语法6-类

    dart语法1dart语法2-内置类型dart语法3-函数dart语法4-操作符dart语法5-异常dart语法6...

网友评论

    本文标题:Dart语法系列(三):函数与异常

    本文链接:https://www.haomeiwen.com/subject/tgecpqtx.html