Dart语法基础之对象,接口,类以及多继承
Dart语言特性
Dart是纯面向对象,基于类,使用可选类型支持混入式继承及Actor模式的并发编程语言。
万物皆对象
Dart是一门纯面向对象的编程语言,这意味着Dart程序在运行时所处理的值都是对象,甚至包括数字,布尔等数据。例如number int double 都是对象类型,举例而言int不主动赋值,默认是null,这就是强大的万物皆对象。
面向接口编程
- Dart的类型基于接口,而不是类。任意类都隐含了一个接口,能够被其他类实现
- Dart没有final方法,允许重写几乎所有的方法
- Dart把对象进行了抽象封装,确保所有外部操作都能通过存取方式来改变对象的状态
- Dart 的构造函数允许对对象进行缓存,或者从子类型创建实例,因此使用构造函数并不意味着绑定一个具体的实现了
类型可选 类C
- 类型在语法层面上是可选的
- 类型对运行时语义没影响
- 类C 选择保守 渐进的修改
在开始内容之前我们先看一个这样的例子吧
import 'dart:math';
class Point{
var x, y;
Point(this.x,this.y);
operator +(p) => new Point(x + p.x, y + p.y);
/// get 的调用语法与变量的访问是没有区别的
/// 实际上dart中所有变量的访问都是调用的get,每个实例变量始终有一个与之关联的get方法
get doubleX => x * 2;
/// 设置值
set newY(newY){
this.y = newY;
}
/// static 修饰符表明此方法不针对某个特定实例,无法访问实例变量x,y
/// static 修饰的类方法是不会被继承的
static distance(Point p1,Point p2){
var dx = p1.x - p2.x;
var dy = p1.y - p2.y;
return sqrt(dx * dx + dy * dy);
}
}
import 'Point.dart';
void main(){
Point pointA = new Point(10, 10);
Point pointB = new Point(10, 10);
Point pointPlus = pointA + pointB;
var x = pointPlus.x;
var y = pointPlus.y;
print('合并后 X = $x,Y = $y');
var doubleX = pointPlus.doubleX;
print('double X = $doubleX');
pointPlus.newY = 100;
print(pointPlus.y);
// 合并后 X = 20,Y = 20
// double X = 40
// 100
}
1.Dart中变量
1.1实例变量
当一个类声明一个实例变量时,它会确保每个实例都有自己的唯一的变量复制。对象的实例变量需要占用内存,这块内存是在对象创建时分配的。Dart会将每个新分配的变量(实例变量,局部变量,类变量和顶层变量)初始化为null。在Dart中,与其他对象一样,null也是一个对象。声明实例或者静态变量会自动引入一个get。如果变量是可变的,则一个set方法也会被自动定义。
如上,对于程序中的变量x,y,我们给予直接赋值也是可以的,
如 point.x = numberA 。dart帮我们定义好了get以及set方法,不必像java还要去针对每个变量设置get以及set方法。
1.2类变量(static变量)
一个类中只有一份类变量的副本,无论他有多少个实例,即使类没有实例,类变量也存在。类变量是延迟初始化(懒加载)的,在getter(get)第一次被调用时类变量才执行初始化,该时机在第一次尝试读取它时。默认初始化为null。
1.3 final变量
表明在初始化后就不能进行修改了。该字段有setter方法,但是没有getter方法。必须保证在任何实例方法运行前就进行初始化。
初始化的时机:
- 声明变量时就初始化
- 构造函数初始化
- 通过初始化列表
- 应当明确不能在构造函数的 { }内设置final变量的值,因为该类型变量无setter,普通变量可以
class FinalClass {
// 1.声明赋值
final String name = 'apple';
final int age;
final bool isMan;
// 2.构造函数初始化
FinalClass(this.age,this.isMan);
}
/// 注意构造函数后的 :
final String name;
final String doubleName;
final int age = 18;
HappyCat(this.name):doubleName = name+name;
1.4 相同与相等
class Girl{
final String name;
final int age;
Girl(this.name, this.age);
@override
bool operator ==(other) {
/// dart中类型判断 is / is!
if(other is! Girl){
return false;
}
/// 强制类型转换
Girl otherGirl = other as Girl;
return otherGirl.name == name && otherGirl.age == age;
}
// 如果2个对象相等,那么哈希码也应该相等
@override
int get hashCode => name.hashCode+age.hashCode;
}
Girl girlA = new Girl('小徐', 18);
Girl girlB = new Girl('小徐', 18);
bool isEquals = girlA == girlB;
print(isEquals.toString());
// true
2. 抽象类与接口
2.1 抽象方法与抽象类
/// 抽象类
abstract class Animal{
final String name;
Animal(this.name){
print('构造函数 Animal');
}
/// 抽象类中可包括实例方法
void eat(){
print('$name 正在吃东西');
}
/// 抽象方法
void hunt();
}
import 'Animal.dart';
class Tiger extends Animal{
Tiger(String name) : super(name){
print('构造函数 Tiger');
}
@override
void hunt() {
print('我用四肢体捕猎物');
}
}
import 'Tiger.dart';
void main(){
Tiger tiger = new Tiger('老虎A');
tiger.eat();
tiger.hunt();
}
//构造函数 Animal
//构造函数 Tiger
//老虎A 正在吃东西
//我用四肢体捕猎物
class A extends B with C 可实现类多继承
2.2 接口
接口继承使用关键字implments,但定义接口仍然使用abstract关键字
abstract class HuntAction{
void hunt();
}
abstract class JoyAction{
void joy();
}
import 'HuntAction.dart';
import 'JoyAction.dart';
class HappyCat implements HuntAction,JoyAction{
final String name;
HappyCat(this.name);
@override
void hunt() {
print('$name 是通过爪子捕猎的');
}
@override
void joy() {
print('$name 喜欢和老鼠玩');
}
}
HappyCat happyCat = new HappyCat('小花');
happyCat.hunt();
happyCat.joy();
///小花 是通过爪子捕猎的
///小花 喜欢和老鼠玩
3.0 构造函数
3.1 重新向构造函数
重定向构造函数的目的是执行重定向到另外一个构造函数。在重定向构造函数中,参数列表跟在一个冒号后面,并以this.constructor(x,y,z) 指定重定向到哪个函数上。
HappyCat(this.name):doubleName = name+name;
// 重定向构造函数不能包含初始化内容
HappyCat.NextParams(String name):this.Params(name,'参数');
HappyCat.OneParams(String name):this(name);
HappyCat happyCat2 = HappyCat.NextParams('小白');
print(happyCat2.name+" "+happyCat2.params);
// 小白 参数
HappyCat happyCat3 = HappyCat.OneParams('王子');
print(happyCat3.name);
// 王子
3.2 工厂构造函数
通常而言,构造函数都会重新分配一份新的实例。而工厂构造函数可以达到类似于缓存的功能。
组织结构与特点:通常由factory前缀开头,它们可能没有初始化列表或初始化形式参数;它们必须又一个返回一个对象的函数体。工厂构造函数可以从缓存中返回对象,或选择分配一个新的实例。
工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象.
工厂构造函数实现单例
class FactoryObject{
String name;
static FactoryObject cache;
// 私有化构造函数
// 类名.方法 实现的是匿名的构造方法
FactoryObject._create(this.name);
factory FactoryObject.getInstance(String name){
if(cache == null){
cache = new FactoryObject._create(name);
}
return cache;
}
}
FactoryObject factoryOne = FactoryObject.getInstance('小王');
print(factoryOne.name); // 小王
FactoryObject factoryTwo = FactoryObject.getInstance('小白');
print(factoryTwo.name); // 小王
print(factoryOne == factoryTwo); // true
4. 其他
4.1 常量对象与字段
q: 何时能够创建常量对象?
先看一个错误的例子
class Point{
int x , y;
Point(this.x, this.y);
}
// **报错** The constructor being called isn't a const constructor
Point point = const Point(10, 11);
正确的例子
class Point{
final int x , y;
// dart语法糖,在构造方法执行前进行赋值,所以可以给final进行初始化
const Point(this.x, this.y);
}
// 正常
Point point = const Point(10, 11);
const 表达式也是调用构造函数,但该构造函数必须是常量构造函数,而且它的常量必须是常量。并且 常量构造函数不能有函数体,他可以有一个初始化列表,前提是只计算常量。