面向对象特性
1.类(class )
Dart是一个面向对象编程语言,同时支持基于mixin的继承机制。每个对象都是一个类的实例,所有的类都继承于object。
1)构造函数
构造函数不能继承。父类的命名构造函数也不会被继承,如果子类也有父类一样命名构造函数,就必须在子类中自己实现该构造函数。
- 默认构造函数:如果未显式定义构造函数,会默认一个参数为空的构造函数。默认构造函数会调用父类的无参构造函数。
class Person { //未定义父类的时候,默认继承自Object
num x;
num y;
num z;
}
void main(List<String> args){
var person = new Person();//调用默认的构造函数
person.x = 10; //使用点(.)引用实例变量或方法
person.y = 11;
}
- 类名构造函数:就是一个与类同名的函数,关键字 this 是指当前实例,只有在命名冲突时有效,否则dart会忽略处理。
class Point {
int x;
int y;
//自己定义的类名构造函数
Point(int x, int y) {
this.x = x;
this.y = y;
}
// 在构造函数里初始化成员属性是很常见的事情,因此Dart开发了新的语法糖来简化这种操作
// 比如将Point的类名构造构造函数改写成
// Point(this.x, this.y);
// 注意x,y的赋值会在构造函数执行之前完成.
}
void main(){
var point = new Point(1, 2);
}
- 命名构造函数:(类名.函数名)使用命名构造函数可以为类提供多个构造函数。
class Point {
num x, y;
Point(this.x, this.y);
// 命名构造函数
Point.origin() {
x = 0;
y = 0;
}
// 简化
// Point.origin(this.x,this.y);
}
- 重定向构造函数:有时构造函数的唯一目的是重定向到同一类中的另一个构造函数。重定向构造函数的主体为空,构造函数调用出现在冒号( :)之后 。
大意就是在创建类时,我定义一个命名构造函数,但是这个构造函式的主体我不实现。直接通过:调用另外一个构造函数,实现对外界传入的参数的接收并赋值给内部的变量。
class Point {
num x, y;
//类名构造函数
Point(this.x, this.y);
// 命名构造函数
Point.order(this.x,this.y);
Point.origin(num a,num b):this.order(a,b);
//重定向构造函数,origin构造函数将外界的传值,指向给了order构造函数。
}
- 工厂构造函数:在实现一个构造函数时使用factory关键字,该构造函数并不总是创建其类的新实例。例如,工厂构造函数可能会从缓存中返回实例,也可能会返回子类型的实例。工厂构造者对this没有访问权限。
class Logger {
final String name;
bool mute = false;
// _cache是私有变量
//_在名称前,表示该变量为私有
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
调用:
var logger = Logger('UI');
logger.log('Button clicked');
- 常量构造函数:如果你的类创建的对象从不改变,你可以创建一些编译时的常量对象。因此,定义一个const构造函数,且保证所有的对象变量都是final。
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
}
注意:如果父类中没有默认的构造函数,你必须手动调用父类的构造函数,在子类的构造函数体之前通过(:) 指定调用父类构造函数,示例如下:
// Person类中没有一个无参数,未命名的构造函数
class Person {
String firstName;
// 命名构造函数
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 你必须调用父类的super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
}
2.实例变量
声明实例变量时,所有未初始化的实例变量的值为null。
每个实例变量都会自动生成一个getter方法,Non-final(没有被final修饰)实例变量还会自定生成一个setter方法。
class Point{
num x;//声明实例变量x,初始化为空
num y;//声明实例变量y,舒适化为空
num z = 0;//声明实例变量z,初始化为0
//如果在实例变量定义的时候初始化该变量(不是在构造函数或者其他方法中初始化),该值是在实例化对象的时
//候初始化的,也就是在构造函数和初始化参数列表执行之前。
}
void main() {
var point = new Point();
point.x = 4; //使用setter方法对x变量赋值
print(point.x == 4); //输出true 使用getter获取x变量的值
print(point.y == null); //输出true
}
静态(类)变量:使用static关键字来实现类级别的变量和方法,静态函数内部不能访问非静态成员,非静态函数能访问静态成员。
class Queue {
// 静态变量在使用之前不会初始化
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
3.方法
1)实例方法
对象的实例函数可以访问this。
import 'dart:math';
class Point {
num x;
num y;
Point(this.x, this.y);
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
2)类方法
使用static 修饰的方法,内部不能访问实例变量,没有访问this的权限。
class Page{
// 添加 static 关键字
static int currentPage = 1;
num size ;
//类方法
static void scorllDown(){
currentPage = 1;
// 访问实例变量报错
// size = 10;
print("ScrollDown...");
}
void scorllUp(){
currentPage ++;
print("ScrollUp...");
}
}
void main(List<String> args) {
var page = new Page();
// 调用类方法
Page.scorllDown();
}
3)Getters和Setters 方法
Getters 和setters是用来设置和访问对象属性的特殊函数。每个实例变量都隐含的具有一个getter,如果变量不是final的则还有一个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;
}
main() {
var rect = new Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
4)抽象函数
抽象函数是只定义函数接口但是没有实现的函数,由子类来实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。
abstract class Doer {
// ...定义实例变量和方法...
void doSomething(); // 定义一个抽象方法.
}
class EffectiveDoer extends Doer {
void doSomething() {
// ...提供实现,因此此处的方法不是抽象的...
}
}
4.抽象类
使用abstract修饰符定义一个抽象类,一个不能被实例化的类。抽象类通常用来定义接口,如果你想实例化抽象类,你必须实现抽象类,才能被实例化。
// 这个类是抽象类,不能实例化
abstract class AbstractContainer {
// ...定义构造函数, 变量, 方法...
void updateChildren(); // 抽象方法.
}
class SpecializedContainer extends AbstractContainer {
// ...定义更多的构造方法, 方法...
void updateChildren() {
// ...实现 updateChildren()...
}
}
5.隐式接口
Dart中没有哪个关键字是来定义接口的, 默认情况下所有的类类都是都是隐式的接口,包括类的方法和属性。当将一个类当做接口使用时,那么实现这个接口的类,必须实现这个接口中所有的方法。
// A person. 隐式接口包含greet().
class Person {
// 在接口中,但仅在此库中可见
final _name;
// 不在接口中,因为这是构造函数
Person(this._name);
// 在接口中
String greet(who) => 'Hello, $who. I am $_name.';
}
// 实现Person 接口
class Imposter implements Person {
// 我们必须定义这个,但我们不使用它。
final _name = "";
String greet(who) => 'Hi $who. Do you know who I am?';
}
6.类的继承
使用extends创建子类,super引用父类,子类可以重写实例方法、getter和setter,使用@override注释重写,使用@proxy注释来忽略警告
class Person {
void run{
print('people run');
}
}
class Kids{
@override
void run{
print('Kids run');
}
}
重写object的noSuchMethod()
当用户调用你定义的类中不存在的属性与方法时,通过重写noSuchMethod(),可以做出一些响应。
class A {
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
7.访问控制
默认类中的所有属性和方法是public的。在dart中,可以在属性和方法名前添加“_”使私有化。
class Animal{
String _name; // 私有属性
int age;
//默认构造函数的简写
Animal(this._name,this.age);
void printInfo(){
print("${this._name}----${this.age}");
}
// 供外部调用私有属性
String getName(){
return this._name;
}
// 私有方法
void _run(){
print('这是一个私有方法');
}
execRun(){
this._run(); //类里面方法的相互调用
}
}
void main(){
Animal a=new Animal('小狗', 3);
print(a.getName());
a.execRun(); //间接的调用私有方法
}
8.枚举类型
枚举中的每个值都有一个index索引,它返回枚举声明中值的从零开始的位置。例如,第一个值具有索引0,第二个值具有索引1。
enum Color { red, green, blue }
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
可以在switch语句中使用枚举,如果不处理枚举的所有值,将会收到警告。
9.泛型
泛型就是定义的一个类型,类型暂不确定,给使用给一个占位符给代替,在使用的时候可以给确定其定义的类型。
main() {
// 如果自己希望List只包含字符串对象。则可以定义为List<String>代表("list of string")。
List Tech = new List<String>();
Tech.addAll(['Android','IOS','Flutter']);
Tech.add(42);//运行时报错
}