1、方法
1.1 一等方法对象
在dart 中是面向对象的编程 , num、String、dynamic、这些类型都是一个对象,当然也包括方法(Function)
看下面的代码, 先定义了一个fun()
方法 ,然后将方法赋值给 Function
变量, 再通过 变量 f
调用
//这是一个方法
void fun() {
debugPrint("这是一个方法");
}
void main() {
//方法都是对象,可以赋值给Function变量
// Function 类,我们将 fun 方法赋值给Function 变量
Function f = fun;
//直接调用
f();
}
上面的方法 fun()
是没有传参数的,如果我们想传参数应该怎么调用呢
//这个 参数是一个 Function对象(也就是说可以传入另一个方法,但是这里并没有指定方法的参数与返回值)
void fun(Function function) {
debugPrint("这是 fun() 方法");
//1、注意这个地方调用里面的参数是可以随便填的,因为我们并没有定义都有什么参数和方法的返回值
function();
}
void main() {
//方法都是对象,可以赋值给Function变量
// Function 类,我们将 fun 方法赋值给Function 变量
Function f = fun;
//直接调用
//2、传入一个匿名函数,这个传入的匿名函数也是可以随便传入参数的
f((){
debugPrint("这是参数方法");
});
}
最后输出:
这是 fun() 方法
这是参数方法
注意这 1 和 2 的地方(匿名函数和被执行的函数),因为没有定义传入的方法参数和返回值,所以如果两边的参数不一致就会抛出异常(当然编译器是不会报错,只有在运行时才会报错)。 那么这个问题如何解决?
这就有了 typedef
关键字,它是用来定义一个类型的。看下面这行代码
typedef void F(String a,int b)
上面这段代码的意思是:定义了一个 F 的类型,这个F的类型其实就是一个方法, 参数 是(String,int)
返回值 是 void
//定义了一个F类型,F 类型其实就是一个方法,参数是(String,int) 返回值是 Void
typedef void F(String a, int b);
//定义一个方法,些方法的参数 是一个 F 类型
void fun2(F function) {
debugPrint("这是 fun2() 方法");
//因为它是一个F类型,所以在调用的时候要按照我们定义的方法去传参数,而不是和上面一样随便传参
function("abc", 2);
}
void main() {
Function f2 = fun2;
//调用 fun2() 方法,并且传入匿名函数(这里的参数必须要和上面定义的F类型一样)
f2((String a, int b) {
debugPrint("这是 Fun2 方法 $a $b");
});
}
当然除了上面这种还有其他的写法,这三种用哪个都行,个人喜欢上面那个
//直接定义在方法上也是可以的
void fun3(void Function(String str, int a) function) {
function("fun3", 123);
}
//这两个方法是一样的
void fun4(void function(String str, int a)) {
function("fun4", 123);
}
1.2 可选位置参数
在Dart中有一些参数是可选得,调用方法的时候即使不传也是可以的
注意可选位置参数是 有顺序的,如果你想给第二个参数传值,那么前面的必须也要传
//可选位置参数,可以设置默认参数
void fun([int? a, int? b = 2]) {
debugPrint("$a");
debugPrint("$b");
}
void main() {
//这是给第一个参数传值
fun(10);
//如果想给第二个参数传就必须保证前面的参数有值
fun(20,22);
}
1.3 可选命名参数
调用命名参数时必须要有形参的变量名,而且有了名字就不用在意位置了。不管是命名参数还是位置参数都是可以设置默认值的
//可选命名参数
void fun2({int i = 1, j = 2}) {
debugPrint("i=$i;j=$j");
}
//调用命名参数时必须要有形参的变量名,而且有了名字就不用在意位置了
fun2(j: 47);
fun2(i: 35, j: 28);
fun2(j: 98, i: 73);
2、类
所有的类都继承Object
2.1 变量
在Dart中是没有 private 关键字的, 如果想让一个变量是私有的使用 _ 开头的命名
class Point {
//私有变量
final String _a = "abc";
int b = 123;
}
2.2 构造方法
- 类的构造方法是可以没有方法体的
- 在Dart中是没有方法重载的,不管是构造方法还是普通的方法
- 构造方法分为 未命名构造 和 命名构造 ,
- 未命名构造 只能有一个, 命名构造 可以有多个(只要名字不同即可)
//类的定义
class Person {
// late 关键字表示 延迟初始化,和Kotlin中 lateinit 一个道理
late String name;
late int age;
//标准的构造函数
Person(this.name, this.age);
//在Dart中未命名的构造只能有一个,可以有无数个命名的构造
Person._() {
name = "";
age = 0;
}
//命名构造
Person.a();
}
2.3 初始化列表
主要功能就是初始化参数,当然也是可以在构造的方法体中进行初始化的
class Point {
String? _a = "abc";
int b = 123;
//命名构造
Point.Map(Map<String, String> map) : _a = map["a"];//初始化参数列表
void printA() {
debugPrint("_a:$_a");
}
}
void main() {
Map<String, String> map = {"a": "aah"};
var p1 = Point.Map(map);
p1.printA();
}
2.4 常量构造函数
在Dart中有两个常量的关键字 const 和 final
- const 编译时常量
- final 运行时常量
const 的性能要比final的要好
class MyPoint {
final int x;
final int y;
//常量构造函数
const MyPoint(this.x, this.y);
}
void main() {
var p1 = const MyPoint(1, 2);
var p2 = const MyPoint(1, 2);
var p3 = const MyPoint(1, 3);
debugPrint("${p1 == p2}"); // 输出结果为true,注意这两个的参数是一样的,如果不一样就为false
debugPrint("${p2 == p3}");//参数不一样为false
}
常量构造函数在其他语言中是没有的。在Dart中是很常见的,这是因为在Dart中控件都可以是一个类.比如我们在不同页面中有三个TextView 显示的内容是一样的,那如果使用常量构造函数创建出来的组件就是同一个对象,而不会产生三个对象
2.5 工厂构造函数 与 静态方法
工厂构造函数 的关键字是 factory
. 必须要有一个构造函数
class Manager {
late int i;
//普通函数
Manager();
//工厂构造函数
factory Manager.get() {
//调用未命名的构造(但是这个构造必须也要有)
return Manager();
}
}
//继承
class Child extends Manager {}
在Dart中静态方法使用 static
,静态方法可以直接使用 类名.方法名()
调用
class C {
static print(obj) {
debugPrint("$obj");
}
}
//调用
void main() {
//通过类名直接调用
C.print(test);
}
2.6 Getter 与 Setter 方法
Dart 语言中的这两个方法都是有对应的 get
和 set
关键字。
get 方法是没有参数列表的,所以不需要 加括号
set 方法必须要声明一个参数
class Child extends Manager {
int _x = 0;
Child() : super();
int get getX {
return _x;
}
set setX(int a) {
_x = a;
}
}
这里当我们调用 set
方法时更像是给变量赋值
var child = Child();
child.setX = 1;
// child.setX(1); 这样写会报错
2.7 操作符重载
在Kotlin中也有这个功能, 这两个语言使用的关键字是一样的
class Child {
int _x = 0;
int get getX {
return _x;
}
set setX(int a) {
_x = a;
}
//返回值和参数可以定义成任何自己需要的类型
int operator +(Child c) {
var child = Child();
child.setX = _x + c.getX;
return child.getX;
}}
调用的时候需要注意前面的是调用者后面的是参数
var child = Child();
child.setX = 3;
var child2 = Child();
child2.setX = 4;
//这里 child 是调用者,child2 是参数,相当于 child.+(child2)
int c = child + child2;
debugPrint("${c}");
下面这个图片是可重载的操作符
可重载的操作符.png
3 、抽象类与接口
3.1 抽象类
- 使用
abstract
表示 抽象类 - 抽象类中没有方法体的就是抽象方法
- 因为构造方法本身是可以省略方法体,所以他不是抽象方法
abstract class A {
//构造方法本身可以省略方法体
A();
//在抽象类中没有方法体的就是抽象方法
void test();
//有方法体的是实例方法
void test2() {
debugPrint("这是 实例方法 test2()");
}
}
class B extends A {
@override
void test() {
// TODO: implement test
}
}
3.2 接口
- Dart 中没有 interface 关键字
- 他的所有的类都可以看做是接口,除了抽象类除外
在Dart中每个类都隐式的定义了一个包含所有实例成员的接口
//创建一个普通的类 Test
class Test {
//变量
int x = 0;
//方法
void fun1() {}
}
//实现 Test 类
class MyTest implements Test {
@override
late int x = 1;
@override
void fun1() {
// TODO: implement fun1
}
}
4、可调用的类
如果一个类中有 call()
方法,可直接调用,代码如下
class MyTest {
// 可调用的类
void call() {
debugPrint("Call 方法");
}
}
void main() {
var test = MyTest();
//直接调用call
test();
//平时这样调用也是可以的
test.call();
}
5、混合 mixins
混合类似是多继承,可以把多个类中的方法混入到新的类中
如下代码,将A,B混入到C类中
class A{
void a(){
debugPrint("A类的 a() 方法");
}
void b(){
debugPrint("A类的 b() 方法");
}
}
class B{
void b(){
debugPrint("B类的 b() 方法");
}
}
//将A,B混入到C类中
//注意混入的顺序
class C with A,B{
@override
void a(){
debugPrint("C类的 a() 方法");
}
}
//如果C类中没有自己的方法的话还可以使用下面的语法糖
class C = Object with A, B;
注意:
- A和B 必须是 Object 的直接子类
- A类和B类不能有构造方法
- 如果存在重复的方法,会先从C类本身中找,如果找不到再去从最后混入的B 类中去找,然后再是A类
6、异常
上面记录了一些类相关的知识,接下来记录一下异常相关
在Dart中可以抛出任何类型的异常,包括但不限于 int
String
bool
Function
对,你没有看错还可以抛出一个方法
//这里的index 动态的数据类型 dynamic
void _throwException(index){
if (index == 0) {
throw "字符串";
} else if (index == 1) {
throw 100;
} else if (index == "2") {
throw print;
}
}
void test(){
print("======");
try{
_throwException("2");
//使用 on 判断 是哪种异常
}on int catch(e,s){
print(e);
print(e.runtimeType);
}on String catch(e,s){
print(e);
print(e.runtimeType);
}on Function catch(e){
e("这是一个方法");
e(e.runtimeType);
}
}
网友评论