Dart 也是一个面向对象的语言。
Dart里面所有的东西都是对象,所有的东西都继承于Object。
基本写法
什么是对象
对象就是属性和方法的集合。
List Set Map
var list1 = List();
list1.add(); //方法
list1.length; //属性
Dart 里面类怎么声明
// 声明不要放在main函数里面
// 首字母必须大写
class Person {
String name = '张三';
int age = 18;
showName() {
print('我的名字叫${this.name}');
}
}
void main() {
var p1 = Person(); //new 可以省略
p1.showName(); //我的名字叫张三
}
构造函数的使用
Dart中的类与Java中的相似,不同的是,Dart中没有private
、public
这些成员访问修饰符。如果是类私有的成员,不希望外面访问,只需要在成员变量之前加上一个下划线_
变为私有即可。
另外注意,Dart中没有构造方法的重载,不能写两个同名的构造方法。
class Person {
String name;
int age;
// 类似js的 constructor ,构造函数名字和类型要一致。只能声明一个
Person(name, age) {
this.name = name;
this.age = age;
}
show() {
print('${this.name} -- ${this.age}');
}
}
void main() {
var p1 = Person('张三', 18);
var p2 = Person('李四', 20);
p1.show(); //张三 -- 18
p2.show(); //李四 -- 20
}
简写
class Person {
String name;
int age;
String province;
String city;
Person(
this.name, {
this.age,
this.sex = 'male',
});
show() {
print('${this.name} -- ${this.age} -- ${this.sex}');
}
}
void main() {
var p = Person('Jack', sex: 'female');
p.show(); //Jack -- null -- female
}
常量构造方法
如果想提供一个状态永远不变的对像,在Dart中,我们可以创建一个编译时常量对象,节省开销。
class ConstPoint {
final num x;
final num y;
// 使用const修构造方法
const ConstPoint(this.x, this.y);
// 编译时常量对象,需使用const来创建对象
static final ConstPoint origin = const ConstPoint(0, 0);
}
void main() {
print(ConstPoint.origin.x); //0
print(ConstPoint.origin.y); //0
}
命名构造函数(命名构造方法)
Dart类中两个同名构造方法不能重载,但是Dart语言为类新增了一种称为命名构造方法
的东西。
例如 DateTime.now()
.
使用命名构造方法可以为一个类实现多个构造方法,也可以更清晰的表明意图。
class Person {
String name;
int age;
String sex;
String province;
String city;
Person(this.name, {this.age, this.sex = 'male'});
// 命名构造函数(自定义构造函数),可以声明多个
// Person.fromData(Map data) {
// this.province = data['province'];
// this.city = data['city'];
// }
Person.getPosition({
this.province = '北京',
this.city,
});
}
void main() {
// var p = new Person.getPosition({'province': '河南', 'city': '漯河'});
// print('${p.province} -- ${p.city}'); //河南 -- 漯河
var p = Person.getPosition(city: '丰台');
print('${p.province} -- ${p.city}'); //北京 -- 丰台
}
构造方法重定向
有时候一个构造方法会调动类中的其他构造方法来实例化,这时候可以使用构造方法重定向。
class Alignment {
final double x;
final double y;
const Alignment(this.x, this.y)
: assert(x != null),
assert(y != null);
static const Alignment topLeft = Alignment(-1.0, -1.0);
// 命名构造方法 重定向 到同名构造方法,中间使用一个冒号
Alignment.bottomTargetX(x) : this(x, -1);
show() {
print('$x -- $y');
}
}
void main() {
Alignment(0.0, 0.0).show(); //0.0 -- 0.0
Alignment.topLeft.show(); //-1.0 -- -1.0
Alignment.bottomTargetX(-0.5).show(); //-0.5 -- -1.0
}
工厂构造方法
有时候可能有一种需求,并不需要每次都创建新的类实例,而是每一种情况,只需要一个实例。
工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象.
工厂构造函数的关键字为factory,下面我们用工厂构造函数写一个只能创造一个实例的类.
- 工厂构造函数类似于static静态成员,无法访问this指针,所以在工厂构造函数中能处理的任务较为有限.
- 使用工厂构造函数时往往需要定义一个命名构造函数用来生产实例.
示例1
class A {
String name;
// 为 A类 创建名为 cache 的静态成员,它将缓存A类的第一个实例.
static A cache;
// 使用关键字factory修饰类同名构造方法
factory A([String name = 'A']) {
// 在工厂构造函数A中,首先判断A.cache是否已经存在实例(即判断是否是第一次实例化),
// 如果存在则返回缓存的实例,不存在则新建一个实例并缓存.
if (A.cache == null) {
A.cache = new A.newObject(name);
}
return A.cache;
}
// A.newObject是一个命名构造函数,用于创建实例.
A.newObject(this.name);
}
void main() {
A a = new A('hello');
print(a.name); //hello
A b = new A('world');
print(b.name); //hello
print(a == b); //true 说明变量a和b指向的是同一个实例对象.
}
示例2
当我们需要创建一个新的对象或者从缓存中取一个对象时,工厂构造方法就派上了用场。
class Logger {
final String name;
// 创建一个静态Map做为缓存
static final Map<String, Logger> _cache = <String, Logger>{};
// 定义一个命名构造方法,用下划线"_"修饰,将构造方法私有化
Logger._internal(this.name);
// 使用关键字factory修饰类同名构造方法
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
// 调用命名构造方法创建新对象
final logger = Logger._internal(name);
_cache[name] = logger; // 存入缓存
return logger;
}
}
}
void main() {
var uiLog = new Logger('UI');
var eventLog = new Logger('event');
}
单例模式
方法1:
class Demo {
//命名构造函数
Demo._internal() {}
//保存单例
static var _instance = Demo._internal();
//工厂构造函数
factory Demo() => _instance;
}
//使用
var d1 = Demo();
var d2 = Demo();
print(d1 == d2); //true
方法2:
class Demo {
//命名构造函数
Demo._internal() {}
//保存单例
static var _instance = Demo._internal();
// 获取单例
// tatic getInstance = _instance;
static getInstance({refresh = false}) {
refresh == true ? _instance._init() : _instance._next();
}
_init() {
//refresh为true时,做一些事情后,再返回该单例对象
print('_init');
return this;
}
_next() {
//refresh为false时,做一些事情后,再返回该单例对象
print('_next');
return this;
}
}
//使用
var d1 = Demo.getInstance(refresh: true); //_init
var d2 = Demo.getInstance(); //_next
print(d1 == d2); //true
初始化列表(默认值)
- 初始化列表位于构造方法的小括号与大括号之间,在初始化列表之前需添加一个冒号。
- 初始化列表是由逗号分隔的一些赋值语句组成。
- 它适合用来初始化
final
修饰的变量 - 初始化列表的调用是在构造方法之前,也就是在类完成实例化之前,因此初始化列表中是不能访问
this
的
正常实例化
class A {
//实例化方法-初始化赋值
A([
this.name = 'jack',
this.age,
this.sex = 'male',
]);
final String name;
final int age;
final String sex;
show() => print('${this.name} -- ${this.age} -- ${this.sex}');
}
void main() {
A('jack', 18).show(); //jack -- 18 -- male
}
class A {
A({
this.name = 'jack',
this.age,
this.sex: 'male',
});
final String name;
final int age;
final String sex;
show() => print('${this.name} -- ${this.age} -- ${this.sex}');
}
void main() {
A(age: 18).show(); //jack -- 18 -- male
}
命名构造函数实例化
class A {
A.now({
this.name = 'jack',
this.age = 18,
});
final String name;
final int age;
show() => print('${this.name} -- ${this.age}');
}
void main() {
A.now().show(); //jack -- 18
}
class A {
A.now({name = 'jack', age = 18})
: this.name = name, //this可省略
age = age;
final String name;
final int age;
show() => print('${this.name} -- ${this.age}');
}
void main() {
A.now(age: 20).show(); //jack -- 20
}
运算符重载
这个特性,又很类似于C++中的运算符重载,在Java中是没用这种概念的。
class Point {
int x;
int y;
Point(this.x, this.y);
// 使用operator关键字,为该类重载"+"运算符
Point operator +(Point p) {
return new Point(this.x + p.x, this.y + p.y);
}
// 为该类重载"-"运算符
Point operator -(Point p) {
return new Point(this.x - p.x, this.y - p.y);
}
}
void main() {
var p1 = new Point(1, 5);
var p2 = new Point(7, 10);
// 重载运算符后,类可以使用“+”、“-” 运算符操作
var p3 = p1 + p2;
var p4 = p2 - p1;
print("${p3.x}, ${p3.y}"); //8, 15
print("${p4.x}, ${p4.y}"); //6, 5
}
Dart中允许重载的运算符如下:
+ |
– |
* |
~/ |
/ |
% |
^ |
< |
> |
<= |
>= |
== |
[] |
[]= |
& |
~ |
<< |
>> |
| |
getter、setter
getter和setter 是用于对象属性读和写的特殊方法。
每个实例变量都有一个隐士getter,通常情况下还会有一个setter。
使用get和set关键字实现getter和setter,能够为实例创建额外的属性。
class A {
A(this.name, this.age);
final String name;
int age;
getAge() => age;
addAge(int val) {
age += val;
}
}
void main() {
var s1 = A('jack', 18);
print(s1.getAge()); //18
s1.addAge(5);
print(s1.getAge()); //23
}
改:
class A {
A(this.name, this.age);
final String name;
int age;
get getAge => age;
// 等同于
// get getAge { return age; }
set addAge(int val) {
age += val;
}
}
void main() {
var s1 = A('jack', 18);
print(s1.getAge); //18
s1.addAge = 5;
print(s1.getAge); //23
}
私有属性和私有方法
js-私有属性:https://www.yuque.com/zhuchaoyang/wrif6k/ewwgff#ukD3f
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。
使用 _xxx 来定义私有属性和方法。
//注意:要使用私有属性和方法,类必须分离出去。
//Person.dart
class Person {
String name;
int _age; //私有属性
Person(this.name, this._age);
show() {
print('${this.name} -- ${this._age}');
}
// 私有方法
_showPrivate() {}
}
//1.dart
import './Person.dart';
void main() {
var p1 = Person('张三', 18);
print(p1.name); //张三
// print(p1._age); //报错
p1.show(); //张三 -- 18 内部可以使用私有属性和私有方法
// p1._showPrivate(); //报错
}
静态的属性和方法
js-静态方法:https://www.yuque.com/zhuchaoyang/wrif6k/ewwgff#QHBZy
- 静态的属性和方法是通过
static
关键字来实现的。 - 静态变量(类变量)对于类级别的状态是非常有用的,可以直接通过类来访问不需要实例化。
- 静态变量只到它们被使用的时候才初始化。
- 静态方法(类方法)不能在实例上使用,因此他们不能访问
this
。 - 静态函数可以当做编译时常量使用。例如,可以将静态方法作为参数传递给常量构造函数。
- 静态方法不能访问非静态的成员,非静态方法可以访问静态的成员。
class Person {
String name;
static int age = 12; //静态属性
Person(this.name);
// 实例方法
show() {
print(this.name);
print(age); //非静态方法可以访问静态的成员。
}
// 静态方法
static showStatic() {
// print(this.name); //报错:静态方法不能访问非静态的成员
print(age);
}
}
void main() {
var p1 = Person('张三');
print(Person.age); //12 直接通过类来访问不需要实例化
Person.age = 10;
print(Person.age); //10
Person.showStatic(); //10
p1.show(); //张三 10
}
继承(扩展类)
js-类的继承:https://www.yuque.com/zhuchaoyang/wrif6k/iinaz1
继承父类的构造函数:
class Parent {
Parent(this.name, this.age);
final int age;
final String name;
show() {
print('${this.name} -- ${this.age}');
}
}
// class Child extends Parent {} //报错:如果只是用extends,不能继承构造函数中的东西
class Child extends Parent {
Child(name, age) : super(name, age); //通过初始化列表,使用super引用父级的构造函数
}
void main() {
var p1 = Parent('张三', 18);
p1.show(); //张三 -- 18
var c1 = Child('李四', 20);
c1.show(); //李四 -- 20
}
继承并且扩展自己:
class Child extends Parent {
// name age 继承自父组件;sex是子组件扩展的参数
Child(name, age, this.sex) : super(name, age);
final String sex;
showSex() {
print(this.sex);
}
}
void main() {
var c1 = Child('李四', 20, 'male');
c1.show(); //李四 -- 20
c1.showSex(); //male
}
class Parent {
Parent({this.key});
final String key;
show() => print(this.key);
}
class Child extends Parent {
Child({
key,
this.title,
}) : assert(title != null),
super(key: key);
final String title;
aa() => print('${this.key} -- ${key} -- ${this.title} -- ${title}');
}
void main() {
var c1 = Child(key: '我是组件key', title: '我是标题');
c1.show(); //我是组件key
c1.aa(); //我是组件key -- 我是组件key -- 我是标题 -- 我是标题
}
重写父级的方法 / 普通方法里调用父级的方法
class Parent {
Parent(this.name, this.age);
final int age;
final String name;
show() {
print('${this.name} -- ${this.age}');
}
}
class Child extends Parent {
Child(name, age, this.sex) : super(name, age);
final String sex;
@override //可写可不写,目的是让人知道show是重写父类的方法
show() {
print('我被重写了: ${this.name} -- ${this.age} -- ${this.sex}');
}
showParent() {
super.show(); //普通方法里调用父级的方法 super.方法名
}
}
void main() {
var c1 = Child('李四', 20, 'male');
c1.show(); //我被重写了: 李四 -- 20 -- male
c1.showParent(); //李四 -- 20
}
抽象类
我们在继承的时候有的时候需要约束子级必须有某个方法,那这个时候就用抽象类,也就是说,其实抽象类可以理解为一个标准。
Dart语言没有提供interface
关键字来定义接口,但是Dart语言中保留了抽象类,同Java,使用abstract
关键字来修饰抽象类。而Dart中的抽象类,实际上就相当于Java中的接口。
// abstract 來创建抽象类
abstract class Parent {
String name = 'jack';
showName(); //抽象方法 抽象方法没有方法体,必须有子级来实现
// showAge(); //如果有多个抽象方法,子级必须都实现
}
class Child extends Parent {
@override
showName() {
//子级必须有这个方法
print(name);
}
}
void main() {
// var p1 = Parent('李四'); //抽象类是不能被实例化的,子类继承抽象类时,必须实现全部抽象方法。
var c1 = Child();
c1.showName(); //jack
}
多态
同一个方法调用执行不同的效果,说白了就是子类继承了父类的方法,但是不同子类执行的效果不同。
abstract class Parent {
String name = '张三';
showName();
}
class Child extends Parent {
@override
showName() {
print('我是Child的name');
}
}
class Worker extends Parent {
@override
showName() {
print('我是Worker的name');
}
}
//showName虽然都是继承自父类的方法,但是Child、Worker执行的效果不一样。
隐式类(接口的定义)
实际上在Dart中,每个类都隐式的定义了一个包含所有实例成员的接口, 并且该类实现了这个接口。
如果要创建一个A类,A要支持B类的API,但是不需要继承B的实现,那么我们可以通过A实现B的接口。
因此,如果我们想实现某个接口,但又不想继承,则可以使用这种隐式接口机制。我们需要用到关键字 implements
来实现接口。
extends抽象类和implements的区别:
- 如果要复用抽象类里面的方法,并且要用抽象类方法约束子类,我们用extends继承抽象类
如果只是把抽象类当作标准的话,我们就用implements ```dart abstract class DataForm { String name; add(); //只继承所有的抽象类方法 del();
// show() {} //不需要继承其它方法,如果有,那么User用implements继承的时候就会报错。 }
class User implements DataForm { @override String name;
@override add() { return null; }
@override del() { return null; } }
<a name="qxj4h"></a>
# 多接口继承的实现
```dart
abstract class InterfaceA {
String a;
aa();
}
abstract class InterfaceB {
String b;
bb();
}
class Demo implements InterfaceA, InterfaceB {
@override
String a;
@override
String b;
@override
aa() {
return null;
}
@override
bb() {
return null;
}
}
mixins
js-mixin模式:https://www.yuque.com/zhuchaoyang/wrif6k/iinaz1#0mtvR
混入不是真正的继承
class A{}
//class Demo1 extends A { //混入的类不能继承其他类
class Demo1 {
String name = '张三';
//Demo1() {} //不能加构造函数,因为下面的Demo3无法使用super链接父级
showName() {
return name;
}
}
class Demo2 {
int age = 18;
}
// class Demo3 extends Demo1, Demo2{} //报错
class Demo3 with Demo1, Demo2 {}
void main() {
var d3 = Demo3();
print('${d3.name} -- ${d3.age}');
}
先继承再混入
class A {
int a;
A(this.a);
}
class Demo1 {
String name = '张三';
showName() {
return name;
}
}
class Demo2 {
int age = 18;
// 当出现相同的方法,注意顺序
// showName() {
// return 2;
// }
}
// 先继承,再混入
class Demo3 extends A with Demo1, Demo2 {
Demo3(a) : super(a);
}
void main() {
var d3 = Demo3(20);
print('${d3.name} -- ${d3.age} -- ${d3.a}'); //张三 -- 18 -- 20
print(d3.showName()); //张三
}
枚举类型
枚举类型也称为 enumerations 或 enums,是一种特殊的类,用于表示数量固定的常量值。
使用enum关键字定义一个枚举类型:
// 枚举中的每个值都有一个index方法,该返回值所在枚举类型定义中的位置(从0开始)。
// Set 是没有下标的
print(Color.red.index); //0
print(Color.green.index); //1
print(Color.blue.index); //2
// 使用枚举的values常量,获取所有枚举值列表(list)
List<Color> colors = Color.values;
print(colors[2]); //Color.blue
// 枚举不能被实例化,mixins或实现。
// var c1 = Color();
枚举中的每个值都有一个index方法,该返回值所在枚举类型定义中的位置(从0开始)。
enum SingingCharacter { lafayette, jefferson }
SingingCharacter _character = SingingCharacter.lafayette;
void main() {
print(_character); //SingingCharacter.lafayette
print(SingingCharacter.lafayette); //SingingCharacter.lafayette
}