类与对象
- 使用关键字class声明一个类
class Person{
}
- 使用关键字new创建一个对象,new可省略
void main() {
var p1 = new Person();
var p2 = Person();
}
- 所有对象都继承于Object类
属性与方法
- 属性默认会生成getter和setter方法
void main() {
var p = new Person();
p.name = "黄家驹";
p.age = 31;
print("name = ${p.name}, age = ${p.age}");
}
class Person{
String name;
int age;
}
打印结果:
name = 黄家驹, age = 31
可以看到属性name和age可以被重新赋值,且可以被获取,即默认生成了setter和getter方法
- 使用final声明的属性只有getter方法
void main() {
var p = new Person();
// p.gender = "Female"; 报错提示:'gender' can't be used as a setter because it's final
print("gender = ${p.gender}");
}
class Person{
String name;
int age;
final String gender = "Male";
}
gender属性使用了final关键字声明,则该属性只有getter方法,不能被重新赋值
-
属性和方法通过.访问
-
方法不能被重载
class Person{
String name;
int age;
final String gender = "Male";
void work(){
}
// void work(int speed){
//
// } 此时会报错,提示方法名已经被定义The name 'work' is already defined.
}
在Java中支持方法的重载,上述代码中有两个相同方法名的方法work,但是第二个需要传入speed参数,这个在Java中的合理的,但是Dart不支持方法的重载,即使方法有不同参数,只要方法名定义过,就不能在重载同名的方法了。
类与成员的可见性
- Dart中的可见性以library(库)为单位
- 默认情况下,每一个Dart文件就是一个库
- 使用_表示库的私有性
- 使用import导入库
将Person类单独写在Person.dart文件中
class Person{
String name;
int age;
final String gender = "Male";
void work(){
}
}
在另一个Dart文件中要调用Person类,则需要使用import导入Person.dart
import 'Person.dart';
void main() {
var p = new Person();
}
当在Person类名前添加_时,该类则不能被其他文件调用
class _Person{
String name;
int age;
final String gender = "Male";
void work(){
}
}
当在属性名前添加_时,该属性不能被直接调用
class Person{
String _name;
int age;
final String gender = "Male";
void work(){
}
}
此时Person类的name属性不能被直接调用。
计算属性
- 顾名思义,计算属性的值是通过计算而来,本身不存储值
- 计算属性赋值,其实是通过计算转换到其他实例变量
计算正方形的面积,在之前我们可以通过定义一个area()方法计算面积,返回边长*边长的值;
void main() {
var square = Square();
square.side = 1.5;
print(square.area());
}
class Square{
num side;
num area() => side * side;
}
这里我们可以把面积定义成一个计算属性:
void main() {
var square = Square();
square.side = 1.5;
print(square.area);
}
class Square {
num side;
num get area => side * side;
}
此时,area是Square类的一个属性而不是方法了,同理,可以设置计算属性area的值,比如知道面积可以通过面积平方根计算出边长的值:
import 'dart:math';
void main() {
var square = Square();
square.side = 1.5;
print(square.area);
square.area = 36;
print(square.side);
}
class Square {
num side;
num get area => side * side;
set area(value){
side = sqrt(value);
}
}
打印结果为:
2.25
6.0
构造方法
- 如果没有定义构造方法,则会有个默认构造方法
- 如果存在自定义构造方法,则默认构造方法无效
- 构造方法不能重载
定义Person类,没有自定义的构造方法,则其会有默认构造方法
class Person{
String name;
int age;
}
等同于:
class Person{
String name;
int age;
Person(){
}
}
当有自定义的构造方法时, 默认构造方法无效,即无法通过Person()创建出实例,而是需要通过传入两个参数的构造方法创建。
class Person{
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
}
由于构造函数传入参数赋值的形式很常见,Dart中提供一种***语法糖***的形式来简化这种操作,改造后如下:
class Person{
String name;
int age;
Person(this.name, this.age);
}
命名构造方法
- 使用命名构造方法,可以实现多个构造方法
- 使用类名.方法 的形式实现
由于Dart中的方法无法被重载,我们只能通过命名构造方法的形式来实现多个构造方法,如下:
class Person{
String name;
int age;
Person(this.name, this.age);
Person.withName(this.name);
Person.withAge(this.age);
}
定义了三个构造方法,其中有两个属于命名构造方法,使用的是 类名.方法 的形式, 创建实例的时候也是类似于构造方法的创建:
void main() {
var p1 = Person("黄家驹", 31);
var p2 = Person.withName("黄家驹");
var p3 = Person.withAge(31);
}
常量构造方法
- 如果类是不可变状态,可以把对象定义为编译时常量
- 使用const声明构造方法,并且所有变量都为final
- 使用const声明对象,可以省略
void main() {
const p = Person("张三", 18);
//p = Person("李四", 20); 此时会报错: Constant variables can't be assigned a value
}
class Person{
final String name;
final int age;
const Person(this.name, this.age);
}
工厂构造方法
- 工厂构造方法类似于设计模式中的工厂模式
- 在构造方法前添加关键字factory实现一个工厂构造方法
- 在工厂构造方法中可返回对象
void main() {
var logger = Logger("tag1");
logger.log("this is a log");
}
class Logger {
final String name;
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name){
if(_cache.containsKey(name)){
return _cache[name];
}else{
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg){
print(msg);
}
}
可以看到Logger中包含一个工厂构造方法,通过判断一个缓存Map中是否包含指定name的Logger对象,如果有则直接取出并返回,没有则调用私用的命名构造方法internal()创建,然后存进map并返回该对象。
初始化列表
- 初始化列表会在构造方法体执行之前执行
- 使用逗号分隔初始化表达式
- 初始化列表常用于设置final变量的值
void main() {
var p = Person.withName("张三");
print("namee = ${p.name}, age = ${p.age}, gender = ${p.gender}");
}
class Person{
String name;
int age;
final String gender;
Person.withName(this.name): age = 20, gender = "female";
}
打印结果为:
namee = 张三, age = 20, gender = female
可以看到age和gender属性被赋值了,而gender属于final,还是可以被赋值,说明初始化列表在构造方法体执行之前执行
静态成员
- 使用static关键字来实现类级别的变量和函数
- 静态成员不能访问非静态成员,非静态成员可以访问静态成员
- 类中的常量需要使用static const声明
void main() {
Person.growUp();
Person.growUp();
Person.growUp();
}
class Person{
static const int maxAge = 100;
static int age = 1;
static void growUp(){
age ++;
print("growUp age = $age");
}
}
打印结果为:
growUp age = 2
growUp age = 3
growUp age = 4
和Java类似,使用static关键字修饰,growUp为静态方法,可以通过类名.方法名直接调用
对象操作符
下面讲到的对象操作符和Kotlin中的差不多,有Kotlin经验的小伙伴已经get到手了。
- 条件成员访问 ?.
void main() {
Person person;
person?.work();
}
class Person{
void work(){
print("work...");
}
}
?.调用方法是在对象不为null的时候才会执行,由于person变量并没有赋值,此时运行没有输出任何语句。
- 类型转换 as
void main() {
dynamic a = "I Love Beyond";
(a as String).split(" ")
}
声明一个dynamic类型的a变量,赋值为字符串,但是不能直接调用到split()方法,因为split()是属于String类的方法,这里需要将其转换为String类型,再调用。
- 是否是指定类型 is、is!
void main() {
dynamic a = 1;
if(a is! bool){
print("a is not bool");
}
a = true;
if(a is bool){
print("a is bool");
}
}
声明一个dynamic类型的a变量,赋值为1,1处判断其a是否不是布尔值类型,由于a是int类型,满足条件,所以会打印"a is not bool",然后再将a赋值为true,此时a为bool值,所以2处的条件语句会满足,会打印 “a is bool”。
对象call方法
如果类实现了call()方法,则该类的对象可以作为方法使用
void main() {
var person = Person();
person("黄家驹", 31);
}
class Person{
String name;
int age;
void call(String name, int age){
print("name = $name, age= $age");
}
}
打印结果为:
name = 黄家驹, age= 31
由于实现了call()方法,所以可以直接把对象作为方法来调用,call()方法也可以定义为有返回值:
void main() {
var person = Person();
print(person("黄家驹", 31));
}
class Person{
String name;
int age;
String call(String name, int age) => "name = $name, age= $age";
}
本章已介绍完毕,感兴趣的小伙伴可以继续查看第四章:
共同学习,写下你的评论
评论加载中...
作者其他优质文章