03 - Dart笔记-面向对象

新公司迭代需求有点猛,拖了很长时间。
计划Dart还有一篇高级特性的笔记

[TOC]

类的使用

通过构造函数实例化

// 非 命名构造函数
var test = Test();
  
// 命名构造函数(named ctor)
var testFeature = TestFeature.fromTest(test);
// 常量构造函数
var constObj1 = const TestConstCtor(1);

常量构造函数

  • const构造函数必须用于成员变量都是final的类
  • 构建常量实例必须使用定义的常量构造函数,如果不是常量构造函数是无法在实例化时使用const修饰符
  • 如果实例化时不加const修饰符,即使调用的是声明为const的构造函数,实例化的也不是常量实例
class TestConstCtor {
  final int x;
  // const构造函数必须用于成员变量都是final的类
  // Can't define a constructor for a class with non-final fields
//  String y;
  const TestConstCtor(this.x);
}

void testConstCtor() {

  // 构建常量实例必须使用定义的常量构造函数,如果不是常量构造函数是无法在实例化时使用const修饰符
  // The constructor being called isn't a const constructor
  //  var test2 = const Test();
  var constObj1 = const TestConstCtor(1);
  var constObj2 = const TestConstCtor(2);
  var constObj2_2 = const TestConstCtor(2);
  // 如果实例化时不加const修饰符,即使调用的是声明为const的构造函数,实例化的也不是常量实例
  var constObj2_3 = TestConstCtor(2);

  print("const TestConstCtor(1) and const TestConstCtor(2) same: ${identical
    (constObj1, constObj2)}"); //false

  print("const TestConstCtor(2) and const TestConstCtor(2) same: ${identical
    (constObj2_2, constObj2)}");//true

  print("TestConstCtor(2) and const TestConstCtor(2) same: ${identical
    (constObj2_2,constObj2_3)}");//false
}

对象的运行时类型

obj.runtimeType返回的是对象的实际类型,即使根据多态的特性将变量声明为父类类型,runtimeType也会返回实际类型

setter/getter

  • 类的成员变量默认生成与字段同名的set/get函数

  • 字面意义上的私有成员变量也会生成set/get函数,在类的外部可以调用

    class TestFeature {
      int c;
      int d;
    
      int _f;
      // ...
    }
    
    test(){
        var testFeature = TestFeature.fromTest(test);
        testFeature._f = 20;
        print("_f = ${testFeature._f}");
    }
    

  • 计算属性的set/get

    <type> get <computedProperty> {
        
    }
    set <computedProperty>(<type> value) {
        
    }
    

构造函数

警告:初始化器的右边部分中无法访问this关键字。

默认构造函数

  • 不声明构造函数则提供默认构造函数
  • 只要有构造函数,即使是命名构造函数,就不会提供默认的构造函数
  • 无参数,非命名
void testDef() {
  // 只要有构造函数,即使是命名构造函数,就不会提供默认的构造函数
  // The class 'TestDefault' doesn't have a default constructor
//  var testDefault = TestDefault();
}

class TestDefault {
  int a;
  int b;

//  TestDefault();
  TestDefault.namedCtor(int c) {
    a = c;
    b = c;
  }
}

带参数的非命名构造函数

  • 直接给类成员变量赋值的语法糖

    class Test {
        int a;
        Test(this.a);
    }
    
  • 构造函数的参数列表与普通函数相同,可以处理无参,必须参数和可选参数等不同的情况

    class TestNotNamed {
      int c;
      int d;
      int e;
      int f;
      
    //  TestNotNamed();
    //  TestNotNamed(this.d);
    //  TestNotNamed(this.c, {this.d, this.e, this.f});
    //  TestNotNamed(this.c, [this.d, this.e, this.f]);
    
      TestNotNamed(int x, int y) {
        c = x + y;
      }
    
    }
    

命名的构造函数

使用命名构造函数可以在一个类中定义多个构造函数,不同的命名构造函数可以专门用于不同的场景:

class Point {
  num x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}

构造函数没有继承

  • 子类不从父类继承构造函数。
  • 如果父类有默认构造函数,则子类任意定义构造函数,默认会调用执行父类的默认构造函数,不必强制使用初始化器列表调用父类的构造函数
  • 父类没有提供默认构造函数,子类须要显示指定使用父类的哪一个构造函数,并传递参数
class TestParent {
  int m;

  TestParent.noArg();
}

class TestChild extends TestParent {
  int x;

  // 不显示定义构造函数则报错
//  TestChild() :super.noArg();
  TestChild.noArg() : super.noArg();
}

构造函数的初始化器

初始化器在构造函数声明和方法体中间。

构造函数向初始化器传递参数

不需要使用this区分入参和类的成员变量

Square(int id, int width, int height)
      : super(id, width: width, height: height);

Square.bySize(int id, int size) : super(id, width: size, height: size);

super调用父类构造函数

  • 父类构造函数初始化器必须是初始化器列表的最后一项
Square(int id, int width, int height)
    : super(id, width: width, height: height);

Square.testInitList(int id)
      : desc = "it's a desc",
        // 重定向构造函数初始化器必须单独使用,不能和域初始化器和父类构造函数初始化器同时使用
        // The redirecting constructor can't have a field initializer
//        this.bySize(id,10),
        super(id)
  // 父类构造函数初始化器必须是初始化器列表的最后一项
  // super call must be last in initializer list
//      ,comment = "a new comment"
  ;

this重定向构造函数

  • 重定向构造函数初始化器必须单独使用,不能和域初始化器和父类构造函数初始化器同时使用
  Rectangle.twiceWidth(int id, int width)
      // 构造函数重定向
      // 初始化器不能使用this,也就是只能使用顶层函数和static静态函数
      : this(id, width: width, height: getTwiceWidth(width));

Square.testInitList(int id)
      : desc = "it's a desc",
        // 重定向构造函数初始化器必须单独使用,不能和域初始化器和父类构造函数初始化器同时使用
        // The redirecting constructor can't have a field initializer
//        this.bySize(id,10)

初始化实例变量

class Square extends Rectangle {
    String comment;
    // ...
    Square.initMember()
      :comment="a comment",
        super(1);
}

工厂构造函数

  • 构造函数前使用factory关键字标识其是工厂构造函数
  • factory 声明的工厂构造函数不能使用this关键字,这种构造函数类似static静态函数
class TestFactory {
  final String factoryName;

  static final Map<String, TestFactory> _cache = <String, TestFactory>{};

  TestFactory._internal(this.factoryName);

  // factory 声明的工厂构造函数不能使用this关键字,这种构造函数类似static静态函数
  // Initializing formal parameters can't be used in factory constructors.
//  factory TestFactory(this.factoryName);

  factory TestFactory(String factoryName) {
    var instance = _cache[factoryName];
    if (null == instance) {
      instance = TestFactory._internal(factoryName);
      _cache[factoryName] = instance;
      return instance;
    } else {
      return instance;
    }
  }

}

类的特性

类的实例方法/set/get/计算属性

  • set/get默认实现,不需要显式声明和实现

  • 如果需要处理set/get,按照特定格式声明set/get

    <type> get <property> {
        
    }
    
    // 显式定义setter时避免设置返回类型
    // avoid return type on setters
    /*void*/ set <property>(<params>) {
        
    }
    
  • 可以通过set/get设置计算属性。但是与Vue不同,调用计算属性的get仍然每次都会执行计算

    class TestMethod {
      int a;
      int b;
      int c;
      int d;
    
      TestMethod(this.a, this.b, this.c, this.d);
    
      int get e {
        // 计算属性在执行getter方法时每次都会执行statement,与Vue的计算属性不同
        print("get e");
        int ret = a + b;
        return ret;
      }
    
      // 显式定义setter时避免设置返回类型
      // avoid return type on setters
      /*void*/
      set e(int vi) {
        a = vi - b;
      }
    }
    

抽象类与抽象方法、隐式接口

  • 抽象类不能(用new)实例化
  • 每个类都提供一个同名的,包含所有方法的隐式接口
  • 接口类的声明使用abstract class XXX
  • set/get 也可以声明为abstract交由子类具体实现
abstract class TestAbstract {
  void methodA() => print("real method");

  void abstractMethod(String text);

  int a;

  int get abstractGet;

  set abstractSet(int value);
}

class TestAbstractImpl extends TestAbstract {
  @override
  // TODO: implement abstractGet
  int get abstractGet => null;

  @override
  void abstractMethod(String text) {
    // TODO: implement abstractMethod
  }

  @override
  void set abstractSet(int value) {
    // TODO: implement abstractSet
  }

//  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

class TestInterface {
  void saySth(String text) {
    print(text);
  }
}

class TestInterfaceImpl implements TestInterface {
  @override
  void saySth(String text) {
    // TODO: implement saySth
  }
}

操作符override

  • 函数名为操作符,函数名前使用operator标识 <returnType> operator <op>(<params>) {}
  • 支持的操作符
< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
- % >>
class TestOverrideOperator {
  int a;
  int b;

  TestOverrideOperator(this.a, this.b);

  TestOverrideOperator operator +(TestOverrideOperator opVal) =>
      TestOverrideOperator(this.a + opVal.a, this.b + opVal.b);

  @override
  String toString() {
    return 'TestOverrideOperator{a: $a, b: $b}';
  }
}

noSuchMethod

重写noSuchMethod()方法来处理程序访问一个不存在的方法或者成员变量

class Foo {
  void sayHi() => print("hi");

  int get money => 123;
}

class TestNoSuchMethod implements Foo {
  void hello() => print("hello");

  int get salary => 777;

  @override
  noSuchMethod(Invocation invocation) {
    var runtimeType = invocation.runtimeType;
    // invocation.memberName 返回的是Symbol对象,
    // #操作符 用于引用一个操作符(方法名)
    if (invocation.memberName == #sayHi) {
      if (invocation.isMethod) {
        return hello();
      }
    } else if (invocation.memberName == #money) {
      if (invocation.isGetter) {
        // 如果调用的是get/set方法,应当返回一个get/set方法的返回值
//        return hello;
        return salary;
      }
    }
    return super.noSuchMethod(invocation);
  }
}

mixin多继承

  • 子类使用with标识符声明其他继承
  • Mixin类必须是Object的直接子类
  • Mixin类不能声明构造函数
  • Mixin类中不能出现super语句
class TestMixin extends Parent with MixinA {
  String b;
}

class Parent {
  int a;
}

class MixinA {
  int c;

//  MixinA(this.c);

  void play(String text) {
    print(text);
//    super.toString();
  }
}

枚举

  • 枚举的values下标从0开始
  • <type>.values返回所有的枚举值
  • enum 使用switch必须把所有枚举都列出,否则报错
void testEnum() {
  // 获取所有的枚举值
  var values = TestEum.values;
  // 枚举的values下标从0开始
  var index2 = TestEum.TypeTwo.index;
  print("values:${values};index2:${index2}");

  var type = TestEum.TypeThree;

  // enum 使用switch必须把所有枚举都列出,否则报错
  switch (type) {
    case TestEum.TypeOne:
      break;
    case TestEum.TypeTwo:
      break;
    case TestEum.TypeThree:
      break;
  }
}

enum TestEum { TypeOne, TypeTwo, TypeThree }

泛型

  • 与java写法类似,可以在类上和方法上使用泛型
  • 运行时不会泛型擦除
  • 泛型标记不能实例化
void testGenericClass() {
  TestGeneric<MyImplA>(MyImplA(3, 5)).printDataName();
  TestGeneric<MyImplB>(MyImplB(41, 8)).printDataName();
}

class TestGeneric<T extends MyInterface> {
  final T data;

  TestGeneric(this.data);

  void printDataName() {
    print("for ${T}:name=${data.getName()}");
  }

// 泛型S不能实例化
//  S newInstance<S extends String>(String message) {
//    return new S(message);
//  }
}

abstract class MyInterface {
  String getName();
}

class MyImplA implements MyInterface {
  int a;
  int b;

  MyImplA(this.a, this.b);

  @override
  String getName() {
    return (a + b).toString();
  }
}

class MyImplB implements MyInterface {
  int c;
  int d;

  MyImplB(this.c, this.d);

  @override
  String getName() {
    return (c * d).toString();
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容