var num = 7;
print(num / 3); // 除法操作, 结果2.3333..
print(num ~/ 3); // 整除操作, 结果2;
print(num % 3); // 取余操作, 结果1;
expr1 ?? expr2
// ..
var p = Person()
..name = "zad"
..eat()
..run();
print(p);
在Dart中,定义类用class关键字。
类通常有两部分组成:成员(member)和方法(method)。
定义类的伪代码如下:
class 类名 {
类型 成员名;
返回值类型 方法名(参数列表) {
方法体
}
}
这里有一个注意点: 我们在方法中使用属性(成员/实例变量)时,并没有加this
;
Dart的开发风格中,在方法中通常使用属性时,会省略this
,但是 有命名冲突
时,this不能省略
;
注意:从Dart2开始,new关键字可以省略
// 1.创建类的对象
var p = new Person(); // 直接使用Person()也可以创建
我们知道, 当通过类创建一个对象时,会调用这个类的构造方法。
没有明确指定构造方法
时,将默认拥有一个 无参的构造方法
。我们也可以根据自己的需求,定义自己的构造方法:
注意一 : 当有了自己的构造方法时,默认的构造方法将会失效,不能使用
不支持函数的重载
(名称相同, 参数不同的方式)。注意二 : 这里我还实现了toString方法
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@override
String toString() {
return 'name=$name age=$age';
}
}
另外,在实现构造方法时,通常做的事情就是通过参数给属性赋值
为了简化这一过程, Dart提供了一种更加简洁的语法糖形式.
上面的构造方法可以优化成下面的写法:
Person(String name, int age) {
this.name = name;
this.age = age;
}
// 等同于
Person(this.name, this.age);
// 命名构造函数
Person.withNameAgeHeight(this.name, this.age, this.height);
Person.fromMap(Map<String, dynamic> map) {
this.name = map["name"];
this.age = map["age"];
this.height = map["height"];
}
class Point {
final num x;
final num y;
final num distance;
// 错误写法
// Point(this.x, this.y) {
// distance = sqrt(x * x + y * y);
// }
// 正确的写法
Point(this.x, this.y): distance = sqrt(x * x + y * y);
}
class Person {
final String name;
final int age;
// 方式一和方式二的区别:
// 方式一: 应用更广泛
// 方式二: 有局限性, 不能使用表达式, 如三目运算符
// 方式一
// Person(this.name, {int age}): this.age = age ?? 10 { }
// 方式二
Person(this.name, {this.age = 10});
}
class Person {
String name;
int age;
Person(this.name, this.age);
Person.fromName(String name) : this(name, 0);
}
main(List<String> args) {
var p1 = const Person('why');
var p2 = const Person('why');
print(identical(p1, p2)); // true
}
class Person {
final String name;
const Person(this.name);
}
注意一:拥有常量构造方法的类中,所有的成员变量必须是final修饰的.
注意二: 为了可以通过常量构造方法,创建出相同的对象,不再使用 new关键字,而是使用const关键字
main(List<String> args) {
var p1 = Person('why');
var p2 = Person('why');
print(identical(p1, p2)); // true
}
class Person {
String name;
// 用来做缓存, 保证同一个参数获取的对象是一样的
static final Map<String, Person> _cache = <String, Person>{};
factory Person(String name) {
if (_cache.containsKey(name)) {
return _cache[name]!;
} else {
final p = Person._internal(name);
_cache[name] = p;
return p;
}
}
Person._internal(this.name);
}
class Person {
String name;
// setter
set setName(String name) => this.name = name;
// set setName(String name) {
// this.name = name;
// }
// getter
String get getName => name;
// String get getName {
// return this.name;
// }
}
无参默认构造方法
(没有参数且与类同名的构造方法)。无参默认构造方法
,则子类的构造方法必须在初始化列表中通过 super
显式调用父类的某个构造方法。class Animal {
int age;
Animal(this.age);
}
class Dog extends Animal {
String name;
// 要实现父类的构造方法
// Dog(String name, int age):name = name, super(age);
Dog(this.name, int age): super(age);
}
abstract class Runner {
int a = 1;
run() {
print('跑');
}
}
abstract class Flyer {
fly();
}
class SuperMan implements Runner, Flyer {
@override
run() {
print('超人在奔跑');
}
@override
fly() {
print('超人在飞');
}
@override
int a = 1;
}
什么是 抽象方法? 在Dart中没有具体实现的方法(没有方法体),就是抽象方法。
abstract class Shape {
getArea();
}
class Circle extends Shape {
double r;
Circle(this.r);
@override
getArea() {
return r * r * 3.14;
}
}
注意事项:
class Vehicle{
num speed = 0 ;
Vehicle(){
print("super Constructor") ;
}
Vehicle.get(){
print("super Run") ;
}
Vehicle.create(){
print("super create") ;
}
void run(){
print("vehicle run") ;
}
void printSpeed(){
print("spped = > $spped") ;
}
}
class Benz implements Vehicle{
@override
void run() {
print("Benz running");
}
@override
void printspped() {
print("benz spped = $spped") ;
}
// 覆盖(实现)成员变量
@override
num speed;
}
在通过implements实现某个类时,类中所有的方法都 必须被重新实现
(无论这个类原来是否已经实现过该方法)。
但是某些情况下,一个类可能希望直接复用之前类的原有实现方案,怎么做呢?
Dart提供了另外一种方案: Mixin混入的方式
mixin关键字
来定义一个类。with关键字
来进行混入。main(List<String> args) {
var superMan = SuperMan();
superMan.run();
superMan.fly();
}
mixin Runner {
final int a = 1;
eat();
run() {
print('在奔跑');
}
}
mixin Flyer {
fly() {
print('在飞翔');
}
}
// implements的方式要求必须对其中的方法进行重新实现
// class SuperMan implements Runner, Flyer {}
class SuperMan with Runner, Flyer {
// 重写父类方法
@override
run() {
print('SuperMan在奔跑$a');
// return super.run();
}
@override
eat() {
print('SuperMan吃饭');
}
}
在Dart中我们使用static关键字来定义:
static String time;
static attendClass() {
print('去上课');
}
enum Colors {
red,
green,
blue
}
枚举类型中有两个比较常见的属性:
main(List<String> args) {
print(Colors.red.index);
print(Colors.green.index);
print(Colors.blue.index);
print(Colors.values);
}
枚举类型的注意事项:
List使用时的泛型写法:
// 创建List的方式
var names1 = ['why', 'kobe', 'james', 111];
print(names1.runtimeType); // List<Object>
// 限制类型
var names2 = <String>['why', 'kobe', 'james', 111]; // 最后一个报错
List<String> names3 = ['why', 'kobe', 'james', 111]; // 最后一个报错
Map使用时的泛型写法:
// 创建Map的方式
var infos1 = {1: 'one', 'name': 'why', 'age': 18};
print(infos1.runtimeType); // _InternalLinkedHashMap<Object, Object>
// 对类型进行限制
Map<String, String> infos2 = {'name': 'why', 'age': 18}; // 18不能放在value中
var infos3 = <String, String>{'name': 'why', 'age': 18}; // 18不能放在value中
如果我们需要定义一个类, 用于存储位置信息Location, 但是并不确定使用者希望使用的是int类型,还是double类型, 甚至是一个字符串, 这个时候如何定义呢?
Location类的定义: Object方式
main(List<String> args) {
Location l1 = Location(10, 20);
print(l1.x.runtimeType); // Object
}
class Location {
Object x;
Object y;
Location(this.x, this.y);
}
Location类的定义: 泛型方式
main(List<String> args) {
Location l2 = Location<int>(10, 20);
print(l2.x.runtimeType); // int
Location l3 = Location<String>('aaa', 'bbb');
print(l3.x.runtimeType); // String
}
}
class Location<T> {
T x;
T y;
Location(this.x, this.y);
}
如果我们希望类型只能是num类型, 怎么做呢?
main(List<String> args) {
Location l2 = Location<int>(10, 20);
print(l2.x.runtimeType);
// 错误的写法, 类型必须继承自num
Location l3 = Location<String>('aaa', 'bbb');
print(l3.x.runtimeType);
}
class Location<T extends num> {
T x;
T y;
Location(this.x, this.y);
}
最初,Dart仅仅在类中支持泛型。后来一种称为泛型方法的新语法允许在方法和函数中使用类型参数。
main(List<String> args) {
var names = ['why', 'kobe'];
var first = getFirst(names);
print('$first ${first.runtimeType}'); // why String
}
T getFirst<T>(List<T> ts) {
return ts[0];
}
- 在Dart中,你可以导入一个库来使用它所提供的功能。
- 库的使用可以使代码的重用性得到提高,并且可以更好的组合代码。
- Dart中任何一个dart文件都是一个库,即使你没有用
关键字library
声明
import语句用来导入一个库,后面跟一个字符串形式的Uri来指定表示要引用的库,语法如下:
import '库所在的uri';
常见的库URI有三种不同的形式
//dart:前缀表示Dart的标准库,如dart:io、dart:html、dart:math
import 'dart:io';
//当然,你也可以用相对路径或绝对路径的dart文件来引用
import 'lib/student/student.dart';
//Pub包管理系统中有很多功能强大、实用的库,可以使用前缀 package:
import 'package:flutter/material.dart';
库文件中内容的显示和隐藏
如果希望只导入库中某些内容
,或者刻意隐藏库里面某些内容
,可以使用show
和hide
关键字
import 'lib/student/student.dart' show Student, Person;
import 'lib/student/student.dart' hide Person;
库中内容和当前文件中的名字冲突
当各个库有命名冲突的时候,可以使用 as关键字
来使用命名空间
import 'lib/student/student.dart' as Stu;
Stu.Student s = new Stu.Student();
library关键字
通常在定义库时,我们可以使用library关键字给库起一个名字。
但目前我发现,库的名字并不影响导入,因为import语句用的是字符串URI
library math;
part关键字
在之前我们使用student.dart作为演练的时候,只是将该文件作为一个库。
在开发中,如果一个库文件太大,将所有内容保存到一个文件夹是不太合理的,我们有可能希望将这个库进行拆分,这个时候就可以使用part关键字了
不过官方已经不建议使用这种方式
了:
mathUtils.dart
文件
part of "utils.dart";
int sum(int num1, int num2) {
return num1 + num2;
}
dateUtils.dart
文件
part of "utils.dart";
String dateFormat(DateTime date) {
return "2020-12-12";
}
utils.dart
文件
part "mathUtils.dart";
part "dateUtils.dart";
test_libary.dart
文件
import "lib/utils.dart";
main(List<String> args) {
print(sum(10, 20));
print(dateFormat(DateTime.now()));
}
export关键字
官方不推荐使用part关键字
,那如果库非常大,如何进行管理呢?
export关键字
在某个库文件中单独导入mathUtils.dart
文件
int sum(int num1, int num2) {
return num1 + num2;
}
dateUtils.dart
文件
String dateFormat(DateTime date) {
return "2020-12-12";
}
utils.dart
文件
library utils;
export "mathUtils.dart";
export "dateUtils.dart";
test_libary.dart
文件
import "lib/utils.dart";
main(List<String> args) {
print(sum(10, 20));
print(dateFormat(DateTime.now()));
}
最后,也可以通过Pub管理自己的库自己的库,在项目开发中个人觉得不是非常有必要,所以暂时不讲解这种方式。
external和@patch关键字