Flutter

Dart 语言和java差异:修饰符、匿名函数、可选参数、级联、

2019-06-13  本文已影响0人  古都旧城

可以不用指定类型

Dart 语言可以不用指定类型,当然也可以指定数据类型(可以提高运行速度)

  var a = "canzhang";
  String b = "canzhang";
  print(a==b);//true

Dart 语言的程序入口

void main() {}

例如flutter的入口

void main() => runApp(new MyApp()); //dart 中单行函数的写法

Drat 语言中没有java的 private public protected概念

但是如果变量或函数以下划线(_)开始,则该函数或变量属于这个dart文件私有(private)的方法。
新建两个dart文件(这里和java的类还是有区别的,这里的dart文件不用像java类一样和主类必须同名)

class Person {
  String _name;//私有
  int age;

  Person(String name, int age) {
    this._name = name;
    this.age = age;
  }

  String getName() {
    return this._name;
  }

  int _getAge() {//私有
    return this.age;
  }
}

同级目录创建main_test.dart

import 'person.dart';

void main() {
  Person person = new Person("canzhang",18);
//  print(person._name); // 会爆红   The getter '_name' isn't defined for the class 'Person'
  print(person.getName());//canzhang
  print(person.age);// 18
//  print(person._getAge());//会爆红   The method 'getAge' isn't defined for the class 'Person'
}

都可以正常引用

void main() {
  Person person = new Person("canzhang",18);
  print(person._name); // 正常使用 canzhang
  print(person.getName());//canzhang
  print(person.age);// 18
  print(person._getAge());//正常使用 18
}


class Person {
  String _name;//私有变量的写法
  int age;

  Person(String name, int age) {
    this._name = name;
    this.age = age;
  }

  String getName() {
    return this._name;
  }

  int _getAge() {//私有方法的写法
    return this.age;
  }
}

变量和基本数据类型

 var a = "canzhang";
 var b = 2;
 var c = false;

这个也可以这么写(这种写法就指定了类型,可以提高编译运行速度)

  String a = "canzhang";
  int b = 2;
  bool c = false;
void main() {
  int a ;
  bool b;
  double c;
  String d;
  print(a==null);//true
  print(b==null);//true
  print(c==null);//true
  print(d==null);//true
}
  final int a  =1;
  a= 0;//报错 'a', a final variable, can only be set once
  const int b = 2;
  b=0;//报错 Constant variables can't be assigned a value

  final int c;//报错 The final variable 'c' must be initialized
  const int d;//报错 The const variable 'd' must be initialized

都是只能被赋值一次,二次赋值会报错,初始化不赋值同样也会报错。

那么这两个值有什么区别呢?
它们的区别在于,const比final更加严格。final只是要求变量在初始化后值不变,但通过final,我们无法在编译时(运行之前)知道这个变量的值;而const所修饰的是编译时常量,我们在编译时就已经知道了它的值,显然,它的值也是不可改变的。
通过下面这个例子,可以对比一下。

import 'dart:math';

void main() {
  final int a = getRandomValue(); //正常
//  const int b = getRandomValue();// 报错 Const variables must be initialized with a constant value
  print(a);
}

//取随机数
int getRandomValue() {
  return new Random().nextInt(100);
}

在上面的例子中,a的值是不变的,但如果不把代码运行起来,你无法知道a是多少。而如果想把一个不确定的值赋值给const 则会直接报错。

   int a =1 ;
   double b = 1.1;
   num c = 1;
   num d = 1.1;

2、String
基本和java一样,有一点不同可以直接兼容单引号和双引号

  String a = "canzhang";
  String b = 'canzhang';
  print(a == b);//true

还有就是可以直接包裹多行内容

  //多行的 可以添加换行符
  String a = "canzhang\n666666";
  //也可以通过""" xxx """ 来包裹多行内容,这里连续三个单引号也是可以的。
  String b = """canzhang
  哈哈哈哈哈
  嘿嘿嘿""";

  print(a);
  print(b);

3、List
最不同的点就是看着写法有点像java中的数组和集合的结合

  List list = [1, 2, 3];
  list.add(666);
  print(list.length);//4
  print(list[list.length - 1]);//666

  List list2 = new List();
  list2.addAll(list);
  print(list2[3]);//666

3、Map

  Map map = {
    "a": 1,
    "b": 2,
    "a": 3, //测试下会不会覆盖
    3: "a"
  };

  print(map.length); //2
  print(map["a"]); //3 同一个key 会覆盖
  print(map["b"]); //2
  print(map[3]); //a 没有添加泛型,可以塞不同类型数据

  //也可以通过new map
  Map map2 = new Map();
  map2["a"] = 1;
  map2["b"] = 2;
  print(map2["a"]);//1

Dart 中的函数

void main() {
  var num = add(1, 2);
  print(num);//3
}

int add(int a, int b) {
  return a + b;
}
void main() {
  print(getMsg("canzhang", "男")); //canzhang 的性别是 男
  print(getMsg("canzhang", "男", 18)); //canzhang 的性别是 男 年龄 18 附带信息  哈哈哈
  print(getMsg("canzhang", "男", 18,"嘿嘿嘿")); //canzhang 的性别是 男 年龄 18 附带信息 嘿嘿嘿
  //这里需要注意位置可选参数必须前面位置已经有对应的值,即时存在默认值也是不行的,前面的参数必须按照顺序一一赋值
  print(getMsg("canzhang", "男", "呵呵呵")); // 报错 The argument type 'String' can't be assigned to the parameter type 'int'
}

String getMsg(String name, String sex, [int age =1 ,String extra =" 哈哈哈"]) {
  String msg = "$name 的性别是 $sex";
  if (age != null) {
    msg = "$msg 年龄 $age";
  }
  if (extra != null) {
    msg = "$msg 附带信息 $extra";
  }
  return msg;
}

void main() {
  print(getMsg("canzhang", "男")); //canzhang 的性别是 男
  print(getMsg("canzhang", "男", age:18)); //canzhang 的性别是 男 年龄 18 附带信息  哈哈哈
  print(getMsg("canzhang", "男", age:18,extra:"嘿嘿嘿")); //canzhang 的性别是 男 年龄 18 附带信息 嘿嘿嘿
  //可选命名参数和可选位置不同,不关心顺序,名字对应上就可以了
  print(getMsg("canzhang", "男",extra: "呵呵呵")); // canzhang 的性别是 男 年龄 1 附带信息 呵呵呵
}

String getMsg(String name, String sex, {int age =1 ,String extra =" 哈哈哈"}) {
  String msg = "$name 的性别是 $sex";
  if (age != null) {
    msg = "$msg 年龄 $age";
  }
  if (extra != null) {
    msg = "$msg 附带信息 $extra";
  }
  return msg;
}
void main() {
  print(getMsg("canzhang", "男")); //canzhang 的性别是 男 年龄 100 (没有传入值的时候使用默认值)
  print(getMsg("canzhang", "男", 18)); //canzhang 的性别是 男 年龄 18
}

String getMsg(String name, String sex, [int age = 100]) {
  String msg = "$name 的性别是 $sex";
  if (age != null) {
    msg = "$msg 年龄 $age";
  }
  return msg;
}
void main() {
  print(doNothing()==null);//true
}

doNothing() {}
main() {
  var child = new Child("canzhang", "18");
  printName(() => child); //canzhang   传递无参数的匿名函数当做入参
  printStr((String s) => s); //xxxx 传递有参数的匿名函数当做入参
  printStr((_) => null); //null 有参数的也可以不传递参数直接使用下划线
}

void printName(Child getChild()) {
  print(getChild().name);
}

void printStr(String getString(String str)) {
  print(getString("xxxx"));
}

一些不同的操作符

import 'person.dart';

void main() {
  var person = new Person("zhangcan", 18);
  var b = 10;
  print(getName(person)); //zhangcan
  print(getName(b)); //不支持类型
}

String getName(var object) {
  if (object is Person) {
    return object.getName();
  }
  return "不支持类型";
}
import 'person.dart';

void main() {
  var person = new Person("zhangcan", 18);
  var b = 10;
  print(getName(person)); //zhangcan
  print(getName(b)); //抛出类型转换异常
}

String getName(var object) {
  return (object as Person).getName();
}
void main() {
  var a = 10;
  var b;
  a ??= 1;
  b ??= 1;
  print(a); // 10  (a不为null,所以不会重新赋值)
  print(b); //1  (b为null,则会赋值)
}
void main() {
  var a =10;
  var b;
  var c = a??b;
  var d = b??a;
  print(c);//10
  print(d);//10
}

示例1:按照如下所示的代码 ... 是没有区别的

void main() {
  var user = new User().setAge(18).setName("canzhang");
  var user02 = new User()
    ..setAge(18)
    ..setName("canzhang");
  print(user.toString()); //User{name: canzhang, age: 18}
  print(user02.toString()); //User{name: canzhang, age: 18}
}

class User {
  String name;
  int age;

  User setName(String name) {
    this.name = name;
    return this;//注意这里返回的是当前对象
  }

  User setAge(int age) {
    this.age = age;
    return this;//注意这里返回的是当前对象
  }

  @override
  String toString() {
    return 'User{name: $name, age: $age}';
  }
}

示例2:把 return this 去掉之后,就出现差别了

void main() {
  var user = new User().setAge(18).setName("canzhang");//报错 The expression here has a type of 'void', and therefore cannot be used
  var user02 = new User()
    ..setAge(18)
    ..setName("canzhang");//正常运行
  print(user.toString()); //User{name: canzhang, age: 18}
  print(user02.toString()); //User{name: canzhang, age: 18}
}

class User {
  String name;
  int age;

  void setName(String name) {
    this.name = name;
  }

  void setAge(int age) {
    this.age = age;
  }

  @override
  String toString() {
    return 'User{name: $name, age: $age}';
  }
}

结论:从上面两个简单的示例我们就可以看出差别了,级联操作符不同于我们在java中的链式调用,dart的级联操作符更强大一些,不需要我们当前的方法返回当前的对象,就可以一直操作这个对象,变的更方便了,代码也更简洁了。

比如要定义一个向量类,让这个类的对象可以直接相加或者相减(使用常规运算符),这个时候就可以重载这俩操作符,并在里面实现自己的逻辑。

//定义一个向量类
class Vector {
  final int x;
  final int y;
  const Vector(this.x, this.y);
 
  //重载加号 + (a + b).
  Vector operator +(Vector v) {
    return new Vector(x + v.x, y + v.y);
  }
 
  //重载减号 - (a - b).
  Vector operator -(Vector v) {
    return new Vector(x - v.x, y - v.y);
  }
}
 
main() {
  //实例化两个向量
  final v = new Vector(2, 3);
  final w = new Vector(2, 2);
 
  final r1 = v + w;
  print('r1.x='+r1.x.toString() + ' r1.y=' + r1.y.toString());
 
  final r2 = v - w;
  print('r2.x='+r2.x.toString() + ' r2.y=' + r2.y.toString());

流程操作语句

void main() {
  var a = 10;
  print("assert ----->>前");//正常执行
  assert(a >= 10);
  print("assert ----->>后");//可以执行到
}
void main() {
  var a = 10;
  print("assert ----->>前");//正常执行
  assert(a < 10);
  print("assert ----->>后");//不能执行到,并且抛出异常:Failed assertion: line 4 pos 10: 'a < 10': is not true.
}

异常处理

void main() {
  var a = 10;
  print("assert ----->>前"); //正常执行
  try {
    assert(a < 10);
  } catch (e) {
    print("exception detail :\n$e"); //异常信息
  } finally {
    print("finally  do somthing");
  }
  print("assert ----->>后"); //可以执行到
}
void main() {
  var a = 10;
  print("assert ----->>前"); //正常执行
  try {
    assert(a < 10);
  } on AssertionError catch (e) {
    print("exception detail :\n$e"); //异常信息
  } finally {
    print("finally  do somthing");
  }
  print("assert ----->>后"); //可以执行到
}
void main() {
  var a = 10;
  try {
    assert(a < 10);
  } on AssertionError catch (e,s) {//使用这个catch方法 s就是异常堆栈信息
    print("异常堆栈信息 :\n$s"); //异常堆栈信息
  } 
}
异常堆栈信息 :
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:40:39)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)
#2      main (file:///C:/Users/canzhang/AndroidStudioProjects/DemoAll/my_flutter/test/my_dart_test.dart:5:12)
#3      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:300:19)
#4      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:171:12)
void main() {
  var a = 10;
  try {
    int c = "canzhang" as int; //抛出CastError 异常
    assert(a < 10); //抛出 AssertionError 异常
  } on AssertionError catch (e) {
    //只捕获AssertionError
    print("AssertionError :\n$e");
  } on CastError catch (e) {
    //只捕获CastError
    print("CastError:\n$e");
  } catch (e) {
    //捕获所有异常
    print("all error:\n$e");
  }
  print("gogogoo");
}
void main() {
  test();//这里不会提示需要捕获异常
}
void test(){//方法也没有提供抛出异常功能
  throw new CastError();
}
void test(){
  throw 1;
  throw false;
  throw "hahahah";
}
void main() {
  try {
    test1();
  } catch (e) {
    print('main 异常信息:\n $e');
  }
}

void test1() {
  try {
    test();
  } catch (e) {
    print('test1 异常信息:\n $e');
    rethrow; // 继续抛出异常Allow callers to see the exception.
  }
}

void test() {
  throw "6666666";
}

日志

test1 异常信息:
 6666666
main 异常信息:
 6666666

面向对象

dart一些皆为对象,对象也都继承于object

class User {
  String name;
  int age;

  User(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

上面代码除了没有修饰符,其他都和java一样,不过在dart中上面的代码可以简化成下面这个样子

class User {
  String name;
  int age;

  User(this.name, this.age);
}
class User {
  String name;
  int age;

  User.test(Map map) {//命名构造函数,从map中取出参数
    this.name = map["name"];
    this.age = map["age"];
  }

  @override
  String toString() {
    return 'User{name: $name, age: $age}';
  }
}

使用

import '../test02/user.dart';

void main() {
  Map map = new Map();
  map["name"] = "canzhang";
  map["age"] = 18;
  var user = new User.test(map);
  print(user.toString()); //User{name: canzhang, age: 18}
}

1、默认情况下,子类中的构造函数调用父类的未命名无参数构造函数,这种场景和java没有什么区别

void main() {
  new Child();//i am from parent
}
class Child extends Parent {
}
class Parent {
  Parent() {
    print("i am from parent");
  }
}

2、如果父类没有未命名的无参数构造函数,则须手动调用父类中构造函数。 在子类的构造函数体之后用冒号(:)指定父类构造函数

void main() {
  print(new Child("canzhang", "18").toString());//Child{name: canzhang, age: 18}
}
class Parent {
  String name;
  String age;

  Parent(String name,String age) {
    this.name = name;
    this.age = age;
    print("i am from parent");
  }

  @override
  String toString() {
    return 'Parent{name: $name, age: $age}';
  }

}
class Child extends Parent {
  Child(String name, String age) : super(name, age);//指定父类构造函数,这里不指定会报错

  @override
  String toString() {
    return 'Child{name: $name, age: $age}';
  }
}

子类也可以这么写,不是必须和调用的父类构造函数入参一样

class Child extends Parent {
  Child() : super(null, null);

  @override
  String toString() {
    return 'Child{name: $name, age: $age}';
  }
}

构造函数是不能继承的,所以子类是不能继承父类的命名构造函数的。如果你希望使用父类中的构造函数创建子类的实例,你必须在子类中实现父类中的构造函数。

class Parent {
  String name;
  String age;

  Parent(String name, String age)
      : name = name,//这个构造函数后面冒号的形式,就是初始化列表
        age = age {
    print("i am from parent");
  }

  @override
  String toString() {
    return 'Parent{name: $name, age: $age}';
  }
}
void main() {
  print(new Child("canzhang", "18").toString());//Child{name: canzhang, age: 18}
}

从结果来看就和上面案例的结果是一样的,好像就是一个赋值并无不同。
到底哪里不同呢,上面这种场景确实没有什么不同,但是初始化列表这个动作是在子类的构造函数运行之前执行的。
比如如果我想在构造函数里面初始化final变量则会报错

class Parent {
  final String name;
  final String age;

  Parent(String name, String age) {
    this.name = name;//'name'can't be used as a setter because it is final
    this.age = age;//'age'can't be used as a setter because it is final
    print("i am from parent");
  }

}

而如果换成构造函数初始化列表形式则可以正常使用。

class Parent {
  final String name;
  final String age;

  Parent(String name, String age)
      : name = name,//正常
        age = age {//正常
    print("i am from parent");
  }
}

异步支持

Dart是单线程模型,也就没有了所谓的主线程/子线程之分。
那异步是怎么弄的?
遇到有延迟的运算(比如IO操作、延时执行)时,线程中按顺序执行的运算就会阻塞,用户就会感觉到卡顿,于是通常用异步处理来解决这个问题。当遇到有需要延迟的运算(async)时,将其放入到延迟运算的队列(await)中去,把不需要延迟运算的部分先执行掉,最后再来处理延迟运算的部分。(这就好比排了个顺序,把有延迟的任务扔到最后面去执行)
具体原理参见:https://www.cnblogs.com/zhujiabin/p/10313065.html

import 'package:dio/dio.dart';
void getHttp() async {
  try {
    print("请求前");
    Response response = await Dio().get("https://raw.githubusercontent.com/gudujiucheng/DemoAll/dev/storage/test.json");
    print(response);
  } catch (e) {
    print(e);
  }
}

main() {
  getHttp();
  print("似曾相识燕归来");
}

可以看到如下输出日志(await 之后延迟的部分会滞后执行)

请求前
似曾相识燕归来
{
  "content":"年年岁岁花相似,岁岁年年人不同![the msg is from gitHup]"
}

简单改造一下,测试下如果任务不耗时会不会依然被排到后面

void getHttp() async {
  try {
    print("请求前");
    String response = await getStr();
    print(response);
  } catch (e) {
    print(e);
  }
}

main() {
  getHttp();
  print("似曾相识燕归来");
  print("似曾相识燕归来");
  print("似曾相识燕归来");
  print("似曾相识燕归来");
  print("似曾相识燕归来");
}

Future<String> getStr() {//就是简单的返回一个字符串,并无太久耗时
  return new Future<String>(() {
    return "666666";
  });
}
请求前
似曾相识燕归来
似曾相识燕归来
似曾相识燕归来
似曾相识燕归来
似曾相识燕归来
666666

结论:从日志可以看出依然被排到了后面。

上一篇下一篇

猜你喜欢

热点阅读