人生富贵驹过隙,惟有荣名寿金石 —— 顾炎武
Dart 类与方法详解
Dart 是一种基于类和 mixin 继承机制的面向对象的语言。 每个对象都是一个类的实例,所有的类都继承于 Object【而且Dart 中的继承和Java的一样都是单继承的语言,不像C++可以多继承】
对象
对象由函数和数据【即方法和实例变量】组成。
方法的调用要通过对象来完成: 调用的方法可以访问其对象的其他函数和属性,使用 . 来引用实例对象的变量和方法
创建对象
创建对象的前提是得有类,那我们就得先定义一个类,定义类的关键字和 Java 语言一样都是 class 关键字
// 定义一个 Person 类
class Person {
String username = ''; // 定义一个用户名
int age = 0; // 定义一个年龄
}
void main() {
// 创建一个Person 对象
Person p = new Person(); // 这个new 关键字是可以省略的,不过就是java写惯了还是这种舒服
p.age = 20; // 当然了返回类型Person 也可以用 var 来代替,这种都是支持的
print(p.age); // 并调用其对象的默认age属性值
}
输出结果
构造函数
上面我们在创建对象的时候没有给与进行赋值,所以Dart也提供了在创建对象的时候进行赋值的方法,通过创建一个与其类同名的函数来声明构造函数
class Person {
String username = ''; // 定义一个用户名
int age = 1; // 定义一个年龄
// 定义一个构造函数
Person(String username,int age){
// 使用 this 关键字引用当前实例
this.username = username;
this.age = age;
}
}
void main() {
Person p = new Person('zhangsan',123); // new 关键字是可选的
print(p.age);
}
输出结果
构造函数语法糖,通常模式下,会将构造函数传入的参数的值赋值给对应的实例变量, Dart 自身的语法糖精简了这些代码
class Person {
String username = ''; // 定义一个用户名
int age = 1; // 定义一个年龄
Person(this.username,this.age);
}
void main() {
Person p = new Person('zhangsan',23);
print(p.age);
}
默认构造函数
在没有声明构造函数的情况下, Dart 会提供一个默认的构造函数。 默认构造函数没有参数并会调用父类的无参构造函数【这一点和 Java 一样】,子类不会继承父类的构造函数。 子类不声明构造函数,那么它就只有默认构造函数,而且在执行主类构造函数之前一定会执行父类的构造函数,毕竟先进行父类初始化,不然哪来的子类呢
常量构造函数
如果该类生成的对象是固定不变的, 那么就可以把这些对象定义为编译时常量。 需要定义一个 const 构造函数, 并且声明所有实例变量为 final,使用常量构造函数,在构造函数名之前加 const关键字
class ImmutablePoint {
static final ImmutablePoint origin = const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
void main() {
ImmutablePoint p = const ImmutablePoint(2, 2);
}
注意:构造两个相同的编译时常量会产生一个唯一的, 标准的实例
命名构造函数
使用命名构造函数可为一个类实现多个构造函数, 也可以使用命名构造函数来更清晰的表明函数意图【这种方式其实在 java 中就是多态的方式,只不过在 Dart 中是不同的名称罢了】
class Person {
String username = ''; // 定义一个用户名
int age = 1; // 定义一个年龄
Person(this.username,this.age);
// 命名构造函数
Person.origin() {
username = 'haha';
age = 1;
}
}
void main() {
Person p = new Person('zhangsan',23);
Person p_1 = new Person.origin();
print(p.age);
print(p_1.age);
}
输出结果
重定向构造函数
有时构造函数的唯一目的是重定向到同一个类中的另一个构造函数。 重定向构造函数的函数体为空,构造函数的调用在冒号:之后
class Person {
String username = ''; // 定义一个用户名
int age = 1; // 定义一个年龄
Person(this.username,this.age);
// 命名构造函数
Person.origin() {
username = 'haha';
age = 1;
}
// 从定向构造函数指向主构造函数
Person.alongXAxis(String username) : this(username, 0);
}
void main() {
Person p = new Person('zhangsan',23);
Person p_1 = new Person.origin();
Person p_2 = new Person.alongXAxis('lallala');
print(p.age);
print(p_1.age);
print(p_2.username);
}
输出结果
工厂构造函数
当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory关键字。 例如,一个工厂构造函数可能会返回一个 cache 中的实例,通过这种方式就提高了效率以及提高了内存的使用率,当内存中有实例的情况下就不需要进行创建新实例了
void main() {
var logger = Logger('UI');
// 调用实例中的方法
logger.log('Button clicked');
}
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);
}
}
方法
方法是为对象提供行为的函数,也就是进行了对象的创建之后就可以调用其对应的方法
Getter与Setter
Getter 和 Setter 是用于对象属性读和写的特殊方法。每个实例变量都有一个隐式 Getter ,通常情况下还会有一个 Setter 。 【java语言中需要自己将其声明出来,不然的话不会有这个方法的】
使用 get和 set关键字实现 Getter 和 Setter ,能够为实例创建额外的属性【长方形案例】
class Rectangle {
num left, top, width, height;
// 构造函数
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right 和 bottom。
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
print(rect.left);
rect.right = 12;
print(rect.left);
}
输出结果
原始的对象实例的默认的 Getter 和 Setter 也许是直接返回成员变量,但是随着需求变化 Getter 和 Setter 可能需要进行计算处理
抽象类
使用 abstract修饰符来定义抽象类,抽象类不能实例化。 抽象类通常用来定义接口,以及部分实现。 如果希望抽象类能够被实例化,那么可以通过定义一个工厂构造函数来实现,抽象类通常具有抽象方法,抽象方法是没有函数体的
// 定义一个抽象的动物类,这个类被定义为抽象类,所以不能被实例化。
abstract class Animal {
// 定义构造行数,字段,方法...
void fly(); // 抽象方法。
}
// 定义一个鸟类进行动物类的继承之后实现其方法
class Bird extends Animal{
void fly(){
print('i can fly');
}
}
void main() {
Bird b = new Bird();
b.fly();
}
输出结果
总结
Dart 提供的类和对象的信息其实和其他面向对象的语言挺像的,比如以下几点:
- 在进行类继承的时候先执行父类的无参构造函数,之后在执行子类的无参构造函数
- 还有就是重定向构造函数和Java中的多态实现特别像
- 常量的构造函数其实和Java中的单例模式差不多
- 工厂构造函数是说白了就是将创建好的对象放入了缓存中罢了,并提高了效率
- 抽象类和抽象方法其实都和java语言提供的相似