美文网首页
Dart语法基础-5 类 和 对象

Dart语法基础-5 类 和 对象

作者: tp夕阳武士 | 来源:发表于2020-09-11 10:25 被阅读0次

1.类的定义

在Dart中,定义类用class关键字。
类通常有两部分组成:成员(member)和方法(method)。
定义类的代码格式:

class 类名 {
  类型 成员名;
  返回值类型 方法名(参数列表) {
    方法体
  }
}

编写一个简单的Person类:

class Person {
  String name;

  eat() {
    print('$name在吃东西');
  }
}

我们来使用这个类,创建对应的对象:

  • 注意:从Dart2开始,new关键字可以省略。
main(List<String> args) {
  // 1.创建类的对象
  var p = new Person(); // 直接使用Person()也可以创建

  // 2.给对象的属性赋值
  p.name = '韩梅梅';

  // 3.调用对象的方法
  p.eat();
}

2.类的构造构造器

2.1 默认构造器与命名构造器

// 定义一个类型:Dog
class Dog {
  //为这个Dog类声明3个属性
  String name;
  String color;
  int age;

  /*
  如果我们没有为这个类定义任何构造器的话,
  系统会为这个类默认添加一个不带参数的构造器
  */
  // Dog() {}

  /*
  如果我们想为这个Dog类添加一个带参的构造器,
  则系统的默认无参构造器会无效;

  注意,此处我把参数写错了命名可选参数;
  这个参数类型可根据自己的需求设定;
   */
  // Dog({String name, String color, int age}) {
  //   this.name = name;
  //   this.color = color;
  //   this.age = age;
  // }

  //带参的写法还可以升级为下面这样:
  Dog({this.name, this.color, this.age}) {}

  //命名构造函数;
  Dog.unlaunched(String name, {String color, int age})
      : this.color = color == null ? '白色' : color,
        this.age = age == null ? 1 : age {
    this.name = name;
  }

  //只读属性;
  int get years => this.age;
}

2.2.重定向构造方法

在某些情况下, 我们希望在一个构造方法中去调用另外一个构造方法, 这个时候可以使用重定向构造方法

main(List<String> args) {
  Person p = Person.fromName('ppp');
  //通过使用重定向构造行数的方式,p.age次数就不会为null;
  print(p.age);

  Person2 p2 = Person2('rrr');
  print(p2.age);
}

class Person {
  String name;
  int age;

  Person(this.name, this.age);
  Person.fromName(String name) : this(name, 1);
}

class Person2 {
  String name;
  int age;

  Person2(String name) : this.fromName(name, 1);
  Person2.fromName(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

2.3常量构造方法

在某些情况下,传入相同值时,我们希望返回同一个对象,这个时候可以使用常量构造方法.
默认情况下,创建对象时,
即使传入相同的参数,创建出来的也不是同一个对象,看下面代码:

  • 这里我们使用identical(对象1, 对象2)函数来判断两个对象是否是同一个对象:
main(List<String> args) {
  var p1 = Person('why');
  var p2 = Person('why');
  print(identical(p1, p2)); // false
}

class Person {
  String name;

  Person(this.name);
}

但是, 如果将构造方法前加const进行修饰,那么可以保证同一个参数,创建出来的对象是相同的,这样的构造方法就称之为常量构造方法.

main(List<String> args) {
  var p1 = const Person('why');
  var p2 = const Person('why');
  print(identical(p1, p2)); // true

//如果是将结果赋值给const修饰的标识符时,const可以省略.
  const p3 = Person('why');
  const p4 = Person('why');
  print(identical(p3, p4)); // true
}

class Person {
  final String name;

  const Person(this.name);
}

常量构造方法有一些注意点:

注意一:拥有常量构造方法的类中,所有的成员变量必须是final修饰的.

注意二: 为了可以通过常量构造方法,创建出相同的对象,不再使用 new关键字,而是使用const关键字
如果是将结果赋值给const修饰的标识符时,const可以省略.

2.4 工厂构造方法

Dart提供了factory关键字, 用于通过工厂去获取对象:

main(List<String> args) {
  final d1 = Dog.allocWithBreed('土狗');
  final d2 = Dog.allocWithBreed('土狗');
  d2.name = '旺财';
  d1.name = '来福';
  print(identical(d1, d2)); //打印结果: true
  print('${d1.name}, ${d2.name}'); //打印结果: 来福, 来福
}

class Dog {
  String name; // 狗名
  String breed; // 狗品种

  //这个地方使用了static
  //_nameCache 和 _breedCache 都是 类属性;
  // _nameCache 和 _breedCache 是Map<String,Dog>的字典类型;

  static final Map<String, Dog> _nameCache = {};
  static final Map<String, Dog> _breedCache = {};

  Dog({this.name, this.breed});

  factory Dog.allocWithNmae(String name) {
    //判断是否使用过此 name 初始化过 Dog实例
    if (_nameCache.containsKey(name)) {
      Dog dog = _nameCache[name];
      return dog;
    } else {
      Dog dog = Dog(name: name);
      _nameCache[name] = dog;
      return dog;
    }
  }

  factory Dog.allocWithBreed(String breed) {
    //判断是否使用过此 breed 初始化过 Dog实例
    if (_breedCache.containsKey(breed)) {
      Dog dog = _breedCache[breed];
      print('使用过$breed\初始化Dog');
      return dog;
    } else {
      Dog dog = Dog(breed: breed);
      _breedCache[breed] = dog;
      print('没使用过$breed\初始化Dog');
      return dog;
    }
  }
}

3.类的继承 / 接口 / 混入(mixin) / 类属性 / 类方法

3.1 基本属性的继承

继承 -- 使用的关键字是: extends
类继承的写法:

// main函数;
main(List<String> args) {
  Person p1 = Person();
  p1.name = '韩梅梅';
  print(p1.name);
  //因为Person 继承于 Animal 所以也有 name属性;
}

class Animal {
  String name;
}

//Person 继承了 Animal 类
class Person extends Animal {}
3.2 父类构造器的重定向使用

如果父类定义了构造,子类必须调用一下父类的构造器,实现父类的初始化;

main(List<String> args) {
  Person p1 = Person('韩梅梅');
  print(p1.name);
}

class Animal {
  String name;

  //父类定义了一个构造器
  Animal(this.name);
}

class Person extends Animal {
  //子类必须调用父类的构造器,实现父类的初始化;
  Person(String name) : super(name);
}

使用父类构造器的方式也可以是下面这种,通过命名构造器来调用

main(List<String> args) {
  // Person p1 = Person('韩梅梅');
  // print(p1.name);

  Person p2 = Person.allocWith('李雷');
  print(p2.name);
}

class Animal {
  String name;

  //父类定义了一个构造器
  Animal(this.name);
}

class Person extends Animal {
  //子类必须调用父类的构造器,实现父类的初始化;
  // Person(String name) : super(name);

  Person.allocWith(String name) : super(name);
}
3.3 类是隐式接口 和 类的混入(mixin)

类是隐式接口 和 混入使用(mixin)

3.4 类属性 和 类方法

类属性和类方法和Swift的语法有点像,在此不做过多阐述;

main(List<String> args) {
  Student.courses = 9;
  String result = Student.playFootball();
  print(result);
}

class Student {
  // 成员变量(成员属性)
  String name;

  //这种属性就是静态属性,也叫做类属性
  static int courses; //科目数量; 所有的学生学习的科目数量是一样的

  static playFootball() {
    return 'Student is playing football!';
  }
}

4.抽象类:(abstract)

Demo:

main(List<String> args) {
  Square s = Square();
  s.getArea();
  print(s.getInfo());
}

//定义一个抽象类;
abstract class Shape {
  //抽象类可以定义一个方法,而不实现
  void getArea();

  String getInfo() {
    return '你获得了一个正方形';
  }
}

class Square extends Shape {
  @override
  void getArea() {
    // TODO: implement getArea
    print('我重载了抽象类Shape中的 getArea 方法');
  }
}

总结:

  • 抽象类就类似于协议,可以声明一个方法名,不用实现,
  • 遵循协议的类去把协议中的方法实现;
  • 抽象类也可以选择把方法的具体实现也写了;
  • 抽象类是不可以实例化的
  • 可以用swift协议的思想来理解抽象类;
4.1 奇怪的Map

当我们进入Map的源码查看是,我们会发现:

abstract class Map<K, V> {
}

这说明 Map也是一个抽象类,但是Map确实可以实例化的,为什么呢?
因为Map的内部有很多factory函数,可以为其创建实例;

external factory Map();

我们看到这里使用了external;
这个修饰符的作用是:把方法的声明和方法的实现拆分到两个不同的地方;
方法的实现用@path来修饰:
Dart语言的这个设计的好处是,使得一个方法的实现,可以根据不同的平台做出不同的实现;

下面我们去到Dart的源码中看看external factory Map();方法的实现,源码路径:flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/map_patch.dart;
使用VSCode 打开map_patch.dart,看里面的源码:

@patch
class Map<K, V> {
  // Factory constructing a Map from a parser generated Map literal.
  // [elements] contains n key-value pairs.
  // The keys are at position 2*n and are already type checked by the parser
  // in checked mode.
  // The values are at position 2*n+1 and are not yet type checked.
  @pragma("vm:entry-point", "call")
  factory Map._fromLiteral(List elements) {
    var map = new LinkedHashMap<K, V>();
    var len = elements.length;
    for (int i = 1; i < len; i += 2) {
      map[elements[i - 1]] = elements[i];
    }
    return map;
  }

  @patch
  factory Map.unmodifiable(Map other) {
    return new UnmodifiableMapView<K, V>(new Map<K, V>.from(other));
  }

  


//这里就是 Map();  的实现;
  @patch
  factory Map() => new LinkedHashMap<K, V>();
}

LinkedHashMap<K,V>();的实现又在另外一个文件:
flutter/bin/cache/dart-sdk/lib/collection/linked_hash_map.dart

abstract class LinkedHashMap<K, V> implements Map<K, V> {
  /// Creates an insertion-ordered hash-table based [Map].
  ///
  /// If [equals] is provided, it is used to compare the keys in the table with
  /// new keys. If [equals] is omitted, the key's own [Object.==] is used
  /// instead.
  ///
  /// Similar, if [hashCode] is provided, it is used to produce a hash value
  /// for keys in order to place them in the hash table. If it is omitted, the
  /// key's own [Object.hashCode] is used.
  ///
  /// If using methods like [operator []], [remove] and [containsKey] together
  /// with a custom equality and hashcode, an extra `isValidKey` function
  /// can be supplied. This function is called before calling [equals] or
  /// [hashCode] with an argument that may not be a [K] instance, and if the
  /// call returns false, the key is assumed to not be in the set.
  /// The [isValidKey] function defaults to just testing if the object is a
  /// [K] instance.
  ///
  /// Example:
  ///
  ///     new LinkedHashMap<int,int>(equals: (int a, int b) => (b - a) % 5 == 0,
  ///                                hashCode: (int e) => e % 5)
  ///
  /// This example map does not need an `isValidKey` function to be passed.
  /// The default function accepts only `int` values, which can safely be
  /// passed to both the `equals` and `hashCode` functions.
  ///
  /// If neither `equals`, `hashCode`, nor `isValidKey` is provided,
  /// the default `isValidKey` instead accepts all keys.
  /// The default equality and hashcode operations are assumed to work on all
  /// objects.
  ///
  /// Likewise, if `equals` is [identical], `hashCode` is [identityHashCode]
  /// and `isValidKey` is omitted, the resulting map is identity based,
  /// and the `isValidKey` defaults to accepting all keys.
  /// Such a map can be created directly using [LinkedHashMap.identity].
  ///
  /// The used `equals` and `hashCode` method should always be consistent,
  /// so that if `equals(a, b)` then `hashCode(a) == hashCode(b)`. The hash
  /// of an object, or what it compares equal to, should not change while the
  /// object is in the table. If it does change, the result is unpredictable.
  ///
  /// If you supply one of [equals] and [hashCode],
  /// you should generally also to supply the other.
  external factory LinkedHashMap(
      {bool Function(K, K)? equals,
      int Function(K)? hashCode,
      bool Function(dynamic)? isValidKey});

  /// Creates an insertion-ordered identity-based map.
  ///
  /// Effectively a shorthand for:
  ///
  ///     new LinkedHashMap<K, V>(equals: identical,
  ///                             hashCode: identityHashCode)
  external factory LinkedHashMap.identity();

  /// Creates a [LinkedHashMap] that contains all key value pairs of [other].
  ///
  /// The keys must all be instances of [K] and the values to [V].
  /// The [other] map itself can have any type.
  factory LinkedHashMap.from(Map<dynamic, dynamic> other) {
    LinkedHashMap<K, V> result = LinkedHashMap<K, V>();
    other.forEach((dynamic k, dynamic v) {
      result[k as K] = v as V;
    });
    return result;
  }

我们在来看看下面的代码:

Map map = Map();
print(map.runtimeType);
//打印结果:_InternalLinkedHashMap<dynamic, dynamic>

相关文章

  • Dart语法基础-5 类 和 对象

    1.类的定义 在Dart中,定义类用class关键字。类通常有两部分组成:成员(member)和方法(method...

  • Flutter 代码规范整理

    Dart 语法简介 Flutter是使用Dart语言开发的。Dart语言是基于类的纯面向对象语言。Dart 中的所...

  • Dart语法 -- [06 - 类和对象]

    Dart是一个面向对象的语言,面向对象中非常重要的概念就是类,类产生了对象。 1.1. 类的定义 在Dart中,定...

  • java学习笔记第一天

    java基础语法 java基础语法 对象:对象是类的一个实例,有状态和行为。 类:类是一个模板,它描述一类对象的行...

  • 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语法基础-5 类 和 对象

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