前言
前几篇文章分别讲解了Dart中的变量、内置类型、函数(方法)、操作符、流程控制语句和异常,对Dart的基本语法已经有了很多的了解,那么今天来说一下Dart中的类。前几篇文章没有看的,建议先看一下前几篇文章。
那些你不知道的Dart细节之变量
那些你不知道的Dart细节之内置类型
那些你不知道的Dart细节之函数(方法)
那些你不知道的Dart细节之操作符、流程控制语句、异常
那些你不知道的Dart细节之类的点点滴滴
那些你不知道的Dart细节之泛型和库
构造函数
前几篇文章中在讲函数(方法)的一篇中提到过,这里再说一下吧,首先来看一下Java中构造函数的写法:
class Point { double x; double y; Point(int x, int y) { this.x = x; this.y = y; } }
下面是dart中的建议写法:
class Point { num x; num y; Point(this.x, this.y); }
命名构造函数
使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图。
class Point { num x; num y; Point(this.x, this.y); //命名构造函数 Point.fromJson(Map json) { x = json['x']; y = json['y']; } }
重定向构造函数
一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。
class Point { num x; num y; Point(this.x, this.y); //重定向构造函数,使用冒号调用其他构造函数 Point.alongXAxis(num x) : this(x, 0); }
初始化列表
在构造函数体执行之前可以初始化实例参数。 使用逗号分隔初始化表达式。初始化列表非常适合用来设置 final 变量的值。
import 'dart:math'; class Point { //final变量不能被修改,必须被构造函数初始化 final num x; final num y; final num distanceFromOrigin; //初始化列表 Point(x, y) : x = x, y = y, distanceFromOrigin = sqrt(x * x + y * y); }
调用超类构造函数
首先来建立一个超类(父类),下面的代码都继承自此类:
class Parent { int x; int y; //父类命名构造函数不会传递 Parent.fromJson(x, y) : x = x, y = y { print('父类命名构造函数'); } }
超类命名构造函数不会传递,如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。
class Child extends Parent { int x; int y; Child.fromJson(x, y) : x = x, y = y, print('子类命名构造函数'); } }
如果超类没有默认构造函数, 则你需要手动的调用超类的其他构造函数,调用超类构造函数的参数无法访问 this。
class Child extends Parent { int x; int y; //若超类没有默认构造函数, 需要手动调用超类其他构造函数 Child(x, y) : super.fromJson(x, y) { //调用父类构造函数的参数无法访问 this print('子类构造函数'); } }
在构造函数的初始化列表中使用 super(),需要把它放到最后。
class Child extends Parent { int x; int y; Child.fromJson(x, y) : x = x, y = y, super.fromJson(x, y) { print('子类命名构造函数'); } }
常量构造函数
定义const构造函数要确保所有实例变量都是final。
const关键字放在构造函数名称之前。
class Point2 { //定义const构造函数要确保所有实例变量都是final final num x; final num y; static final Point2 origin = const Point2(0, 0); //const关键字放在构造函数名称之前,且不能有函数体 const Point2(this.x, this.y); }
工厂构造函数(Dart中的单例)
工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象。如果一个构造函数并不总是返回一个新的对象(单例),则使用 factory 来定义这个构造函数。工厂构造函数无法访问this。
class Singleton { String name; //工厂构造函数无法访问this,所以这里要用static static Singleton _cache; //工厂方法构造函数,关键字factory factory Singleton([String name = 'singleton']) => Singleton._cache ??= Singleton._newObject(name); //定义一个命名构造函数用来生产实例 Singleton._newObject(this.name); }
Setter和Getter
在Java中get和set方法可以直接生成,在Dart中无需自己定义。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。可以通过实行 getter 和 setter 来创建新的属性, 使用 get 和 set 关键字定义 getter 和 setter。可以开始使用实例变量,后来可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。下面是代码实例:
class Rectangle { num left; num top; num width; num height; Rectangle(this.left, this.top, this.width, this.height); num get right => left + width; set right(num value) => left = value - width; num get bottom => top + height; set bottom(num value) => top = value - height; }
抽象类(接口)
不能被实例化,除非定义一个工厂构造函数。
这个很好理解,在Java中的抽象类也同样不可实例化。
抽象类通常用来定义接口, 以及部分实现。
在Dart中没有interface这个关键字,只有abstract,所以可以使用abstract来生成接口:
abstract class Demo{ void zhujiang(); } class Zhu implements Demo{ @override void zhujiang() {} }
抽象类通常具有抽象方法,抽象方法不需要关键字,以分号结束即可。
其实上面代码也可以用作这个实例,只需要把implements变成extends即可:
class Zhu extends Demo{ @override void zhujiang() {} }
接口方式使用时,需要重写抽象类的成员变量和方法,包括私有的。
一个类可以implement一个普通类。Dart任何一个类都是接口。
一个类可以implement多个接口。
最后几个说的其实有点绕,说白了就是可以实现多个,这里需要注意的是:implement只是实现接口,你需要重写接口中的方法,不然是不会执行的。还有一点是如果想extents多个类的话需要使用with关键字。说了这么多不如直接看代码,看代码应该好理解一些:
abstract class Demo{ void zhujiang(); } abstract class Demo2{ void zhujiang(); } abstract class Demo3{ void zhujiang(); } class Zhu extends Demo with Demo2,Demo3 implements Demo3,Demo2{ @override void zhujiang() {} }
可调用类
实现call()方法可以让类像函数一样能够被调用。这个很简单,直接上代码:
class ClassFunction { call(String a, String b, String c) => '$a $b $c!'; } main() { var cf = new ClassFunction(); var out = cf("aaa","flutter","damon"); print('$out'); print(cf.runtimeType); print(out.runtimeType); print(cf is Function); }
下面是打印结果:
lib/5.dart: Warning: Interpreting this as package URI, 'package:darttest/5.dart'. aaa flutter damon! ClassFunction String false
Mixin
子类没有重写超类A方法的前提下,如果2个或多个超类拥有相同签名的A方法,那么子类会以继承的最后一个超类中的A方法为准。
如果子类自己重写了A方法则以本身的A方法为准。
在这里先不过多解释Minxin,我的理解就类似于策略模式,抽出不变的为接口,然后多实现。这里先不写了,如果有可能的话等专门写一篇介绍Dart中的Minxin的文章吧。
总结
到这里为止Dart的第五篇文章完成。本篇文章主要讲解了一下Dart语言的类。下一篇文章讲解一下Dart中的泛xing和导入库,希望大家能够喜欢