Flutter是一套跨平台方案,使用Dart语言设计。当已经掌握了一门开发语言之后,再去学习别的语言就是非常快的,因为基础语法基本上所有的语言都是一样的,因此只需要进行对比一下差异点就行。
Dart
Dart作为强类型语言,和Java的语法基本上是一致的,然后又和Kotlin也差不多,感觉类似于二者的混合。
属性声明
属性声明和Java一样,类型在前数据名称在后,结尾带分号。但是也可以省略类型,此时会自动推断类型,和Kotlin一致。同时也支持空类型。
1 2 3 4 5 6 7 8 9
| int a = 1; int? b = 2;
var c = "text"; final d = "text1"; String e = "text2";
late int f;
|
作用域
Dart中没有public、private这种访问属性,默认就是public的,如果想要设置为私有的,则只需要在方法名或者属性名或类名前加_即可。
1 2 3 4
| class _Test { int _number = 10; void _test() {} }
|
如上声明了一个私有类,内部有一个私有属性和私有方法,这里的私有作用域是声明类的这个文件。在同一个文件内是可以随便访问的。
函数参数
函数类型分为必选参数、可选参数、命名参数。这和Kotlin中的函数参数差不多,Kotlin中函数参数不加默认值的话就属于必选参数,并且所有参数都属于命名参数。但是在Dart中将二者进行了区分。
1 2 3
| void _test(int number, [String sex = "男"] ) {
}
|
普通参数放在前面,可选参数放在后面,并使用中括号括起来,并且可选参数必须要有默认值。
1 2 3 4
| void main() { _test(10); _test(10, "女"); }
|
调用时不传可选参数会使用默认的值。
1 2 3
| void _test(int number, {required String sex, bool child = false} ) {
}
|
命名参数同样放在普通参数后面,命名参数必须提供默认值,如果不提供默认值的话类型必须是可空的,此时会自动使用null作为默认值,如果不可空并且不提供默认值,则需要使用required来表明该参数是必选的。
1 2 3 4
| void main() { _test(10, sex: "女"); _test(10, sex: "女", child: true); }
|
注意在传命名参数时,必须要带上参数的名称,如sex和child名称,普通参数不需要带。一个函数中不能同时出现可选参数和命名参数。
构造函数
1 2 3 4 5 6 7 8
| class Person { int _age = 0; String _name = ''; Demo(int age, String name) { this._age = age; this._name = name; } }
|
注意,普通构造函数和Java一样,但是只能存在一个构造函数,不能重载。像这种基础的赋值操作,可以在构造函数中进行简化。
1 2 3 4 5 6 7
| class Person { int _age; String _name; Person(this._age, this._name); }
|
通过在构造方法中使用this._age形式,构造对象时可以直接给_age赋值,并且声明该非空属性的地方也不需要设置默认值了,另外,如果不需要做其他操作的话,方法体也可以直接省略了。
1 2 3 4 5 6
| class Person { int? age; String? name; Person({this.age, required this.name}) {} }
|
同样的,构造函数的参数也可以是命名参数,但是命名参数不能是私有参数,私有的只能作为普通参数存在。
1 2 3 4 5 6 7 8 9
| class Person { int? age; String? name; Person(this.age, this.name); }
class Man extends Person { Man(int age, String name): super(age, name); }
|
对于继承类,需要在构造方法后面调用super,而不是在方法体中调用。当然也可以继续简化。
1 2 3
| class Man extends Person { Man(super.age, super.name); }
|
直接在构造参数中通过super.age的方式给父类注入参数,这样就不需要单独调用super()方法了。另外,如果还有额外的参数不想通过构造方法注入的话,可以直接在构造方法的后面给他赋值。
1 2 3 4
| class Man extends Person { Stirng sex; Man(super.age, super.name): sex = '男'; }
|
注意只有自己的参数才能在构造函数的后面赋值,父类的参数只能在构造方法参数中赋值或者super方法中赋值。
命名构造函数,可以设置多个,通过名称来进行区分需要构造什么样的对象。
1 2 3 4 5 6 7 8
| class Person { int? age; String? name; Person(this.age, this.name); Person.adult(this.name): age = 18; Person.child(this.name): age = 10; }
|
命名构造函数实际上就是分不同的场景去创建不同的对象,例如上述例子中的Person的命名构造函数中,只需要传入name,而age则根据不同的场景设置为不同的值,处理不同的初始化逻辑等。
工厂构造方法,可以控制对象的创建过程,可以说工厂构造函数更类似于Java中的静态方法,本身并不是对象的构造函数,而是提供对象的工厂函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Person { int? age; String? name;
Person(this.age, this.name);
factory Person.newPerson(int age, String name) { return Person(age, name); } static Person? _instance; factory Person.defaultInstance() { _instance ??= Person(10, '默认'); return _instance!; } }
|
getter/setter
除了可以和Java一样通过编码两个普通函数实现getter和setter外,Dart还支持对应的关键字来实现。
1 2 3 4 5 6 7
| class Person { int _age; Person(this.age); int get age => _age; set age(int age) => _age = age; }
|
对于私有属性_age,通过get关键字声明了一个age访问属性,进而可以访问到_age的值。另外也通过set关键字声明了一个age()方法来设置值。
1 2 3 4 5 6 7
| void main() { final person = Person(10, "ja"); print('age=${person.age}'); person.age = 20; }
|
拓展方法
和Kotlin一样,Dart也允许为某一个类拓展出不同的方法和属性,通过关键字extension .. on ..实现。
1 2 3 4 5 6 7 8 9 10
| extension PersonExt on Person { String get name => _name; set name(String name) => _name = name; void printPerson() { print("age=$_age, name=$_name"); } }
|
拓展方法在使用上和普通方法是一样的,直接通过对象进行调用即可。
1 2 3 4 5 6
| void main() { final person = Person(10, 'ja'); person.printPerson(); }
|
注意,上面的拓展方法中用到了类的私有属性,这是因为他们定义在了同一个文件中,如果不是同一个文件的话,是无法访问到它的私有属性的。
implements
Dart也是单继承的语言,同样也是使用的extends关键字。另外,它没有接口类,但它能把普通类当做接口类来implements,不论这个类是普通类还是抽象类,而且还支持多实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class Person { int _age; String _name; Person(this._age, this._name); void test() {} }
abstract class User { void say(); }
class Woman implements Person, User { @override int _age = 10;
@override String _name; Woman(this._age, this._name);
@override void test() { } @override void say() { } }
|
当继承(extends)一个类时,能够继承它的方法属性以及对应的逻辑,而当实现(implements)一个或多个类时,需要重写属性和方法,相当于只保留了它们的声明,而丢弃了实现。
混入
Dart中用mixin表示一个功能,该功能可以混入到类中,也就是将某个具体方法逻辑的实现抽取出来,作为一个mixin,然后添加给想要该功能的类。
有点像kotlin的代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| interface Fly { fun fly() }
class FlyImpl : Fly { override fun fly() { print("fly") } }
class Birds: Fly by FlyImpl() { }
|
而在Dart中则是通过混入实现的:
1 2 3 4 5 6 7 8 9 10
| mixin Fly { void fly() { print("fly"); } }
class Birds with Fly {}
|
通过with关键字,在声明类时将Fly混入类给加入到Birds中,这样就不需要在该类中重新写一遍具体的实现了。
而混入类比Kotlin代理更强的一点是,它可以指定在某个类上混入,从而可以访问这个类的属性和方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Person { int age; String name; Person(this.age, this.name); }
mixin Speak on Person { void speak() { print("my name is $name, age is $age"); }
String language = "chinese"; }
class Man extends Person with Speak { Man(super.age, super.name); }
|
即在声明混入类时,通过on关键字指明该混入类只能作用在具体的类的子类上,这样其他类就无法应用该混入类了,与此同时该混入类中也能访问到类的属性和方法了。
总结
有了一门编程语言基础后,再去学另一门语言是非常快的,因为大部分语言都是相通的,只需要关注一下它们的不同之处即可。就像在已经掌握了Java和Kotlin之后,再去学Dart就发现非常容易,因为Dart中的概念在Java和Kotlin中都有,无非就是实现方式不一样而已。