一:方法
Dart语言中定义方法的时候,如果不定义参数类型,可以传入任何类型。并且Dart中不支持方法重载。
例如:三种定义方法的方式
int fun(int i, int j) {
return i + j;
}
//也可以不写参数类型,即为Object类型参数
fun( i, j) {
return i + j;
}
//对于只有一个表达式的方法,可以选择使用简写语法来定义:
//在方法体后加入 =>表达式;(只能使用一个表达式)
fun(i, j) => i + j;
匿名方法
有返回值匿名方法
//调用fun方法,传入一个方法
String str = fun((String str) {
//执行anonymityMethod方法,返回数据
return str;
});
String fun(String anonymityMethod(String str)) {
//调用anonymityMethod方法
return anonymityMethod("调用匿名方法");
}
无返回值匿名方法
fun((String str) {
print(str);
});
String fun(void anonymityMethod(String str)) {
anonymityMethod("调用匿名方法");
}
一等方法对象
Dart语言是一门面向对象的语言,方法也是对象,类型为Function。
Function对象代码方法
void fun(String str1, String str2) {
print("fun方法-------" + str1 + str2);
}
//方法也是对象,类型是Function
Function f = fun;//fun方法是Function类型对象
f("参数1", "参数2");//调用fun方法
在Dart中的方法也是对象,类型是属于Function类型的。那么如果方法的参数为方法呢?那该如何表示?
/**
* fun1方法参数为Function类型,那么该如何调用fun1方法呢?
*/
void fun1(Function f) {
f("参数");//❌运行报错,调用fun1时传入的匿名方法有两个参数,所以会参数异常
f(1, "参数");//Function无法表示传递而来的这个方法参数有几个,所以无法编译时检查
}
Function f1 = fun1;
f1();//❌报错,参数fun1需要一个Function类型参数,所以参数异常
//调用fun1方法,传入一个匿名方法,有点像lamda表达式
f1((int i, String s) {
return bool;
});
在调用参数类型为Function的方法时,可以传入匿名方法,这样使用起来比较麻烦。而且Function无法表示方法的参数和返回值,所以编译时检查不出来,运行时才报错。这样使用起来不是很方便,那该如何解决这个问题呢?
typedef关键字
/**
* typedef关键字:
* 定义"DEF"方法类型,此时"DEF"其实就是一个含有int、String参数和String返回值的方法类
* 型,DEF就可以表示方法的参数和返回值。如此就有了编译时检查。
*/
typedef String DEF(int i, String s);
String fun2(DEF def) {//如此就有编译时检查了
return def(1, "参数");
}
//fun3方法表示的含义与fun2一致,只是fun2使用了typedef关键字,更加简洁
String fun3(String method(int i, String s)) {
return method(1, "参数");
}
使用场景
在点击事件、接口回调等场景中使用
//定义click方法类型;
typedef void onClick();
class Button {
//对外暴露设置点击事件方法,把点击后需要执行的方法传递进来
void setOnClickListener(onClick onClickMethod) {
onClickMethod();//调用方法
}
}
默认值参数
可以在方法体中初始化参数默认值。如果该方法没有被传入值为null,则使用默认值
/**
* 默认值参数,可以设置参数的默认值
*/
void fun({int i = 1, String str = "我是默认值"}) {
}
void fun([int i = 1, String str = "我是默认值"]) {
}
可选位置参数
使用“[]”包裹方法体。如果方法有多个参数,想给某个参数传值,那么该参数前的参数都要传值。只能按照顺序传值。
//调用fun方法
fun(1);
fun(1, "传递一个参数");
/**
* 可选位置参数
* 如果想给String str传值,那int i就必须传值
*/
void fun([int i, String str]) {
}
可选命名参数
使用“{}”包裹方法体,可以根据参数名称进行传值。调用方法时,使用“:”进行分割参数名称和传入参数,以键值对方式传入参数。
//调用fun方法,给参数名为“str”的参数赋值
fun(str:"可选位置参数");
fun(i:3);
/**
* 可选命名参数,使用参数名称给参数赋值
*/
void fun({int i, String str}) {
}
使用场景:可以不需要像Java一样,写很多重载方法,并有默认值。
二:异常
抛出异常
Dart 异常是非检查异常。 方法无需声明了他们所抛出的异常, 调用方法时也不要求你捕获任何异常。
Dart中提供了 Exception和Error类型, 以及一些子类型。你还可以定义自己的异常类型。
Dart中可以抛出任何非null对象为异常,不仅仅是实现了 Exception 或者Error 的对象。
/**
* 抛出异常
*/
String test() {
//抛出Exception
throw new Exception("抛出Exception");
//抛出自定义对象
throw new MyClass();
//抛出String
throw "抛出String";
//抛出int
throw 100;
//抛出方法
throw testMethod();
}
捕捉异常
Dart中捕获异常同样是使用catch语句,catch()方法可以带有一个或两个参数,第一个参数为抛出的异常对象, 第二个为堆栈信息 ( StackTrace 对象)。
on关键字:Dart中的catch无法指定异常类型,需要结合on来使用。
rethrow关键字:可以把捕获的异常给重新抛出
/**
* catch不同类型的异常
* catch的参数:exc、exd
* 此时catch的参数无需设置类型,参数数量也可以是1个或者2个
*/
void main() {
try {
test();
} catch(exc, exd) {//catch所有类型 异常
print(exc);//抛出的异常对象
print(exd);//StackTrace 调用栈信息
} on Exception catch(exc, exd) {//catch指定类型 Exception
} on String catch(exc) {//catch指定类型 String
} on Function catch(exc) {//catch指定类型 方法
rethrow;//捕获的异常重新抛出
}
}
三:类
Dart的命名规范
源文件:lowercase_with_underscores 风格来命名库和文件名。例如:my_dart_file
类名:UpperCamelCase风格来命名类名。例如:MyClass
方法名:lowerCamelCase。例如:myCustomMethod
变量、常量:lowerCamelCase。例如:myCompanyName
Dart的中的访问修饰符_
Dart中只有两种访问范围,使用下划线开头是私有属性,相当于private,Dart中只有public或private。
修饰类:_类名;
修饰方法:_方法名;
修饰变量:_变量名;
举个栗子
//公共范围(public)
public String str;//Java中
String str;//Dart中
//私有范围(private)
private String str;//Java中
String _str;//Dart中
构造方法、可选参数构造方法、命名构造方法
Dart中简化了构造方法。并且由于Dart中不支持方法重载。所以构造方法也不可以重载,由此产生了命名构造方法;
class Person {
String name;
String _age;
//构造方法
Person(this.name, this._age);
//可选参数构造方法
Person({String name, String age}) {
this.name = name;
this._age = age;
}
//命名构造方法,相对于可选参数构造方法,表明的含义更加清晰
Person.name(this.name);
Person._age(this._age);
}
参数初始化列表
在构造方法的方法体执行之前会先执行初始化列表
/**
* 在构造方法的参数体之后,方法体之前,使用冒号分割。给本类变量赋值。
*/
Person.initFromBean(Person person):name = person.name, _age = person._age;
Person.initName(String name): name = name;
Person.initAge(int age):_age = age {}
重定向构造方法
重定向构造方法是没有方法体的,在构造方法的参数体后,使用冒号调用其他构造方法。
使用场景:例如自定义View中,构造方法自己调用自己。
//调用
View view = View.context(10);
class View {
int context;
int attr;
int style;
//重定向调用,命名构造方法View.attr
View.context(int context):this.attr(context, 666);
//重定向调用,构造方法View
View.attr(int context, int attr):this(context, attr, 234);
//构造方法
View(this.context, this.attr, this.style);
}
常量构造方法
使用const关键字修饰构造方法,并且所有全局变量声明为final。
使用const关键字来创建对象,当你创建时传入的参数一样,无论你创建几个对象,它们其实都是同一个对象。
class Demo {
final int width;
final int height;
/**
* 常量构造方法
*/
const Demo(this.width, this.height);
}
void main() {
//使用new来创建,常量构造方法的对象和普通对象没有区别
Demo demo1 = new Demo(1, 1);
Demo demo2 = new Demo(1, 1);
print(demo1 == demo2);//false
print(demo1.hashCode == demo2.hashCode);//false
/**
* 使用const来创建对象,传递的参数一样,对象必须有常量构造方法
* 代表它们是同一个编译期常量对象。
*/
Demo demo3 = const Demo(1, 1);
Demo demo4 = const Demo(1, 1);
print(demo3 == demo4);//true
print(demo3.hashCode == demo4.hashCode);//true
}
工厂构造方法
使用factory关键词修饰的构造方法时,这个构造方法必须返回,类的实例或者子类的实例。但是可以不创建新的实例。
例如:工厂构造函数可能从缓存中获取一个实例并返回,或者返回一个子类型的实例。(工厂构造函数无法访问 this)
/**
* static和factory修饰构造方法产生的效果差不多。
* 但是他们的本质是不一样的,factory修饰属于构造方法,必须返回类或子类的实例对象,
* 而static修饰则是方法,如果不指明返回值是可以不返回实例对象的。
*/
class Parent {
//构造方法
Parent();
/**
* 工厂构造方法
* 使用factory关键字修饰一个命名构造方法
* 返回一个子类实例
*/
factory Parent.getChild() {
return new Child();
}
/**
* 静态方法
* 返回一个子类实例
*/
static Child getChild1() {
return new Child();
}
}
工厂构造方法写单例
//调用
SingleInstance instance = SingleInstance.getInstance();
/**
* Dart单例
*/
class SingleInstance{
static SingleInstance _instance;
/**
* 命名构造方法 + 私有化
*/
SingleInstance._newInstance();
/**
* 工厂构造方法 + 命名构造放,返回对象单例
*/
factory SingleInstance.getInstance() {
if(_instance == null) {
//调用 私有命名构造方法 创建对象
_instance = new SingleInstance._newInstance();
}
//返回单例实例
return _instance;
}
String getString() {
return "我是返回值";
}
}
get、set方法
实例对象的非私有属性变量(非final、非私有),都会有一个隐式的get\set方法,无需写get\set方法。
View view = new View();
view.height = 1000;//set赋值
int height = view.height;//get获取
class View {
//实例对象的非私有属性变量,会有一个隐式的get\set方法,无需写get\set方法
int place;
int _width;
int _height;
int get width => _width;
set width(int value) {
_width = value;
}
int get height => _height;
set height(int value) => _height = value;
}
运算符重载
使用operator关键字,可以把已有操作符进行重新定义,可重载的运算符有:
/**
* 使用重新定义的>操作符,来比较view1和view2两个对象,传入的参数面积大小。返回bool类型
*/
bool b = view1 > view2;
class View {
int _width;
int _height;
int get width => _width;
set width(int value) {
_width = value;
}
int get height => _height;
set height(int value) => _height = value;
//重新定义 >操作符,比较两个View的面积
bool operator>(View otherView) {
bool b = height * width > otherView.height * otherView.width;
return b;
}
}
抽象类
使用 abstract 修饰符定义一个抽象类。抽象类中允许出现无方法体的方法
/**
* abstract 声明抽象类
*/
abstract class Person{
String name;
void play();//抽象方法,不需要在方法前声明abstract,可以没有方法体
void sleep() {}//也可以有正常的方法
Person(this.name);//父类构造方法
}
//extends 继承抽象类
class Child1 extends Person {
Child1(String name) : super(name);//子类构造
@override
void play() {//实现抽象方法
}
}
class Child2 extends Person {
Child2(String name) : super(name);//子类构造
@override
void play() {//实现抽象方法
}
@override
void sleep() {//重写父类方法
super.sleep();//使用super调用父类sleep方法
}
}
接口
Dart中没有interface关键字。在Dart的类中,都隐式的定义了一个包含所有实例成员的接口。
使用implements关键字,可以实现这个类的所有实例成员。不能实现抽象类
(Dart中的类,即可以是类,也可以是接口。分不清了🤣🤣🤣🤣)
class Listener{
static String name;
String _age;
static Function onLongClick() {//静态方法
}
void onClick(){//普通方法
}
}
/**
* 使用implements实现接口
*/
class MyInterface implements Listener {
@override
void onClick() {//实现普通方法
}
@override
String _age;
}
接口与继承的区别
1、单继承,多实现。
2、继承可以有选择的重写父类方法并且可以使用super,实现强制重新定义接口所有成员。
可调用的类
如果Dart 类实现了call()方法,在对象实例后加括号,可以直接调用到call()方法。如果没有实现call方法则会报错。
CallDemo myCall = new CallDemo();
String str = myCall("我是字符串");//myCall对象后加括号,调用call方法;
class CallDemo {
String str;
String call(String str) {
this.str = str;
return this.str;
}
}
混合mixins
Mixins 是一种在多类继承中,重用一个类代码的方法。
使用with关键字,后面跟着需要混入的类。要注意的是,被混入的类不能有构造方法。
混入与继承是兼容的,可以同时使用extends和with关键字。同时也可以使用super关键字,来调用被混入的方法和继承的方法。
混入方法时,如果存在重复的方法。
1、优先调用自身的方法
2、如果自身没有对应方法,就从with最右边开始查找。
C c = new C();
c.a();//c类中混入了A类,所以可以调用a方法
c.b();//c类中混入了B类,所以可以调用b方法
c.c();//调用自身的c方法
class A {
void a() {//被混入的方法a
print("a~~~~~~~");
}
}
class B {
void b() {//被混入的方法b
print("b~~~~~~~");
}
}
abstract class D {//抽象方法
void d() {
}
//void e();//也可以混入抽象方法,这里为了记录super关键字,就不演示了
}
/**
* 继承D类
* 混入B、A类
*/
class C extends D with B, A{
@override
void d() {//重写父类D的d方法
super.d();//调用父类D的d方法
}
void c() {
super.a();//调用被混入的方法a
print("c~~~~~~~");
}
}
总结:
mixins弥补了接口和继承的不足,继承只能单继承,而接口无法复用实现,mixins却可以多混入并且能利用到混入类的具体实现。