新公司迭代需求有点猛,拖了很长时间。
计划Dart还有一篇高级特性的笔记
[TOC]
类的使用
通过构造函数实例化
// 非 命名构造函数
var test = Test();
// 命名构造函数(named ctor)
var testFeature = TestFeature.fromTest(test);
// 常量构造函数
var constObj1 = const TestConstCtor(1);
常量构造函数
- const构造函数必须用于成员变量都是final的类
- 构建常量实例必须使用定义的常量构造函数,如果不是常量构造函数是无法在实例化时使用const修饰符
- 如果实例化时不加const修饰符,即使调用的是声明为const的构造函数,实例化的也不是常量实例
class TestConstCtor {
final int x;
// const构造函数必须用于成员变量都是final的类
// Can't define a constructor for a class with non-final fields
// String y;
const TestConstCtor(this.x);
}
void testConstCtor() {
// 构建常量实例必须使用定义的常量构造函数,如果不是常量构造函数是无法在实例化时使用const修饰符
// The constructor being called isn't a const constructor
// var test2 = const Test();
var constObj1 = const TestConstCtor(1);
var constObj2 = const TestConstCtor(2);
var constObj2_2 = const TestConstCtor(2);
// 如果实例化时不加const修饰符,即使调用的是声明为const的构造函数,实例化的也不是常量实例
var constObj2_3 = TestConstCtor(2);
print("const TestConstCtor(1) and const TestConstCtor(2) same: ${identical
(constObj1, constObj2)}"); //false
print("const TestConstCtor(2) and const TestConstCtor(2) same: ${identical
(constObj2_2, constObj2)}");//true
print("TestConstCtor(2) and const TestConstCtor(2) same: ${identical
(constObj2_2,constObj2_3)}");//false
}
对象的运行时类型
obj.runtimeType
返回的是对象的实际类型,即使根据多态的特性将变量声明为父类类型,runtimeType也会返回实际类型
setter/getter
类的成员变量默认生成与字段同名的set/get函数
-
字面意义上的私有成员变量也会生成set/get函数,在类的外部可以调用
class TestFeature { int c; int d; int _f; // ... } test(){ var testFeature = TestFeature.fromTest(test); testFeature._f = 20; print("_f = ${testFeature._f}"); }
-
计算属性的set/get
<type> get <computedProperty> { } set <computedProperty>(<type> value) { }
构造函数
警告:初始化器的右边部分中无法访问this关键字。
默认构造函数
- 不声明构造函数则提供默认构造函数
- 只要有构造函数,即使是命名构造函数,就不会提供默认的构造函数
- 无参数,非命名
void testDef() {
// 只要有构造函数,即使是命名构造函数,就不会提供默认的构造函数
// The class 'TestDefault' doesn't have a default constructor
// var testDefault = TestDefault();
}
class TestDefault {
int a;
int b;
// TestDefault();
TestDefault.namedCtor(int c) {
a = c;
b = c;
}
}
带参数的非命名构造函数
-
直接给类成员变量赋值的语法糖
class Test { int a; Test(this.a); }
-
构造函数的参数列表与普通函数相同,可以处理无参,必须参数和可选参数等不同的情况
class TestNotNamed { int c; int d; int e; int f; // TestNotNamed(); // TestNotNamed(this.d); // TestNotNamed(this.c, {this.d, this.e, this.f}); // TestNotNamed(this.c, [this.d, this.e, this.f]); TestNotNamed(int x, int y) { c = x + y; } }
命名的构造函数
使用命名构造函数可以在一个类中定义多个构造函数,不同的命名构造函数可以专门用于不同的场景:
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
构造函数没有继承
- 子类不从父类继承构造函数。
- 如果父类有默认构造函数,则子类任意定义构造函数,默认会调用执行父类的默认构造函数,不必强制使用初始化器列表调用父类的构造函数
- 父类没有提供默认构造函数,子类须要显示指定使用父类的哪一个构造函数,并传递参数
class TestParent {
int m;
TestParent.noArg();
}
class TestChild extends TestParent {
int x;
// 不显示定义构造函数则报错
// TestChild() :super.noArg();
TestChild.noArg() : super.noArg();
}
构造函数的初始化器
初始化器在构造函数声明和方法体中间。
构造函数向初始化器传递参数
不需要使用this区分入参和类的成员变量
Square(int id, int width, int height)
: super(id, width: width, height: height);
Square.bySize(int id, int size) : super(id, width: size, height: size);
super调用父类构造函数
- 父类构造函数初始化器必须是初始化器列表的最后一项
Square(int id, int width, int height)
: super(id, width: width, height: height);
Square.testInitList(int id)
: desc = "it's a desc",
// 重定向构造函数初始化器必须单独使用,不能和域初始化器和父类构造函数初始化器同时使用
// The redirecting constructor can't have a field initializer
// this.bySize(id,10),
super(id)
// 父类构造函数初始化器必须是初始化器列表的最后一项
// super call must be last in initializer list
// ,comment = "a new comment"
;
this重定向构造函数
- 重定向构造函数初始化器必须单独使用,不能和域初始化器和父类构造函数初始化器同时使用
Rectangle.twiceWidth(int id, int width)
// 构造函数重定向
// 初始化器不能使用this,也就是只能使用顶层函数和static静态函数
: this(id, width: width, height: getTwiceWidth(width));
Square.testInitList(int id)
: desc = "it's a desc",
// 重定向构造函数初始化器必须单独使用,不能和域初始化器和父类构造函数初始化器同时使用
// The redirecting constructor can't have a field initializer
// this.bySize(id,10)
初始化实例变量
class Square extends Rectangle {
String comment;
// ...
Square.initMember()
:comment="a comment",
super(1);
}
工厂构造函数
- 构造函数前使用
factory
关键字标识其是工厂构造函数 - factory 声明的工厂构造函数不能使用this关键字,这种构造函数类似static静态函数
class TestFactory {
final String factoryName;
static final Map<String, TestFactory> _cache = <String, TestFactory>{};
TestFactory._internal(this.factoryName);
// factory 声明的工厂构造函数不能使用this关键字,这种构造函数类似static静态函数
// Initializing formal parameters can't be used in factory constructors.
// factory TestFactory(this.factoryName);
factory TestFactory(String factoryName) {
var instance = _cache[factoryName];
if (null == instance) {
instance = TestFactory._internal(factoryName);
_cache[factoryName] = instance;
return instance;
} else {
return instance;
}
}
}
类的特性
类的实例方法/set/get/计算属性
set/get默认实现,不需要显式声明和实现
-
如果需要处理set/get,按照特定格式声明set/get
<type> get <property> { } // 显式定义setter时避免设置返回类型 // avoid return type on setters /*void*/ set <property>(<params>) { }
-
可以通过set/get设置计算属性。但是与Vue不同,调用计算属性的get仍然每次都会执行计算
class TestMethod { int a; int b; int c; int d; TestMethod(this.a, this.b, this.c, this.d); int get e { // 计算属性在执行getter方法时每次都会执行statement,与Vue的计算属性不同 print("get e"); int ret = a + b; return ret; } // 显式定义setter时避免设置返回类型 // avoid return type on setters /*void*/ set e(int vi) { a = vi - b; } }
抽象类与抽象方法、隐式接口
- 抽象类不能(用new)实例化
- 每个类都提供一个同名的,包含所有方法的隐式接口
- 接口类的声明使用
abstract class XXX
- set/get 也可以声明为abstract交由子类具体实现
abstract class TestAbstract {
void methodA() => print("real method");
void abstractMethod(String text);
int a;
int get abstractGet;
set abstractSet(int value);
}
class TestAbstractImpl extends TestAbstract {
@override
// TODO: implement abstractGet
int get abstractGet => null;
@override
void abstractMethod(String text) {
// TODO: implement abstractMethod
}
@override
void set abstractSet(int value) {
// TODO: implement abstractSet
}
// noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class TestInterface {
void saySth(String text) {
print(text);
}
}
class TestInterfaceImpl implements TestInterface {
@override
void saySth(String text) {
// TODO: implement saySth
}
}
操作符override
- 函数名为操作符,函数名前使用
operator
标识<returnType> operator <op>(<params>) {}
- 支持的操作符
< | + | | | [] |
> | / | ^ | []= |
<= | ~/ | & | ~ |
>= | * | << | == |
- | % | >> |
class TestOverrideOperator {
int a;
int b;
TestOverrideOperator(this.a, this.b);
TestOverrideOperator operator +(TestOverrideOperator opVal) =>
TestOverrideOperator(this.a + opVal.a, this.b + opVal.b);
@override
String toString() {
return 'TestOverrideOperator{a: $a, b: $b}';
}
}
noSuchMethod
重写noSuchMethod()方法来处理程序访问一个不存在的方法或者成员变量
class Foo {
void sayHi() => print("hi");
int get money => 123;
}
class TestNoSuchMethod implements Foo {
void hello() => print("hello");
int get salary => 777;
@override
noSuchMethod(Invocation invocation) {
var runtimeType = invocation.runtimeType;
// invocation.memberName 返回的是Symbol对象,
// #操作符 用于引用一个操作符(方法名)
if (invocation.memberName == #sayHi) {
if (invocation.isMethod) {
return hello();
}
} else if (invocation.memberName == #money) {
if (invocation.isGetter) {
// 如果调用的是get/set方法,应当返回一个get/set方法的返回值
// return hello;
return salary;
}
}
return super.noSuchMethod(invocation);
}
}
mixin多继承
- 子类使用
with
标识符声明其他继承 - Mixin类必须是Object的直接子类
- Mixin类不能声明构造函数
- Mixin类中不能出现super语句
class TestMixin extends Parent with MixinA {
String b;
}
class Parent {
int a;
}
class MixinA {
int c;
// MixinA(this.c);
void play(String text) {
print(text);
// super.toString();
}
}
枚举
- 枚举的values下标从0开始
-
<type>.values
返回所有的枚举值 - enum 使用switch必须把所有枚举都列出,否则报错
void testEnum() {
// 获取所有的枚举值
var values = TestEum.values;
// 枚举的values下标从0开始
var index2 = TestEum.TypeTwo.index;
print("values:${values};index2:${index2}");
var type = TestEum.TypeThree;
// enum 使用switch必须把所有枚举都列出,否则报错
switch (type) {
case TestEum.TypeOne:
break;
case TestEum.TypeTwo:
break;
case TestEum.TypeThree:
break;
}
}
enum TestEum { TypeOne, TypeTwo, TypeThree }
泛型
- 与java写法类似,可以在类上和方法上使用泛型
- 运行时不会泛型擦除
- 泛型标记不能实例化
void testGenericClass() {
TestGeneric<MyImplA>(MyImplA(3, 5)).printDataName();
TestGeneric<MyImplB>(MyImplB(41, 8)).printDataName();
}
class TestGeneric<T extends MyInterface> {
final T data;
TestGeneric(this.data);
void printDataName() {
print("for ${T}:name=${data.getName()}");
}
// 泛型S不能实例化
// S newInstance<S extends String>(String message) {
// return new S(message);
// }
}
abstract class MyInterface {
String getName();
}
class MyImplA implements MyInterface {
int a;
int b;
MyImplA(this.a, this.b);
@override
String getName() {
return (a + b).toString();
}
}
class MyImplB implements MyInterface {
int c;
int d;
MyImplB(this.c, this.d);
@override
String getName() {
return (c * d).toString();
}
}