[Flutter]足够入门的Dart语言系列之面向对象:类的定义详解、成员和实例使用

简介: 类表示的是分类,一类问题或事物,它是对具体或现实世界的抽象。比如动物类、犬科动物类、猫科动物类、房子类、数学类,类是具体事物的描述,它不是指具体的某个动物、某栋房子、某个数学题,而是对它们的概括...

表示的是分类,一类问题或事物,它是对具体或现实世界的抽象。比如动物类犬科动物类猫科动物类房子类数学类等,类是具体事物的描述,它不是指具体的某个动物、某栋房子、某个数学题,而是对它们的概括。

上面的定义虽然比较正式化,但它却能明确的表达编程中的含义,一个可以描述这个事物,另一个就可以描述另一个事物(只是抽象程度可能有所不同),由此可以推出,可以用描述任何事物。可以抽象现实中任何事物(包括非现实中的),的具体化就是一个具体的特指的对象,任何事物可以用描述为一个对象,这就是面向对象的开发思想。

不仅描述数据或状态,通常还包含行为或功能,用来"完整"的表示一类事物,完成指定的功能逻辑。

类class详细介绍

类的定义

class关键字用来定义一个,后面的标识符表示类的名称。名称后面,通过 {} 表示类定义的作用域,在其中可以定义类的 成员变量成员方法

class ClassName{
    // 类中的成员
}

比如我们定义一个MyAdd实现加法功能的类。

class MyAdd{
    double a=0;
    double b=0;

    double sum(){
        return a+b;
    }
}

类的实例化

定义了类就需要使用,使用类就要创建类对应的具体的对象。

创建对象的过程称为类的实例化,对象是类的一个实例。

var myClassName=ClassName();

比如,我们创建加法类MyAdd的实例,并使用其中的成员:

var myadd=MyAdd();

myadd.a=10;
print(myadd.sum()); // 10
dart提供可选的 new关键字来创建对象: var myClassName=new ClassName();

成员方法

成员变量表示类有哪些数据或状态;成员方法表示类有哪些功能或行为。

MyAdd类的成员方法sum用于实现计算和的功能。通过.进行方法的调用。

var sum=myadd.sum();

成员方法的作用就是对类的逻辑功能的封装,可以达到直接调用,使用对应功能的目的。

构造函数

声明一个与类名一样的函数即可声明一个构造函数。构造函数的作用是用于创建一个类的实例,实现类的实例化,创建一个具体的对象。

构造函数的特定

构造函数是一个特殊的函数:

  1. 构造函数在声明时无返回值。
  2. 构造函数可以没有方法体。
  3. 在参数列表中可以通过 this.成员 ,为成员变量进行赋值。
  4. 没有显式声明构造函数时,默认自动生成一个无参、无方法体的构造函数。

比如上面的MyAdd类,其对应的完整类定义如下,两者等同:

class MyAdd{
    double a=0;
    double b=0;

    MyAdd();
    // ...
}

声明一个有参的构造函数,函数体内实现对属性的赋值。

class MyAdd{
    double a=0;
    double b=0;

    MyAdd(double a,double b){
        this.a=a;
        this.b=b;
    }
    // ...
}
this关键字引用当前实例,指代的是当前对象。

上面使用this是因为参数和成员变量有命名冲突,为了区分才显式的使用this。如果没有冲突,通常不需要指定this

使用时,构造函数需要传入正确的参数。

var myadd = MyAdd(10,6);

参数的初始化形式

构造函数的参数有简化的初始化形式,而不用在构造函数体内进行赋值。

如下,这是为实例变量赋值简化的语法糖:

class MyAdd {
  double a;
  double b;

  MyAdd(this.a,this.b);
}

初始化列表

构造函数可以在执行函数体之前,初始化实例变量。

MyAdd(double a, double b)
    : this.a=a,
    this.b=b;

通过在构造函数的右括号有加:,后面跟随对变量的赋值,多个变量之间用逗号分隔。

在开发模式下,HIA可以在初始化列表中使用 assert 来验证输入数据

MyAdd(this.a, this.b): assert(a>0&&b>0){
  print('参数需要大于0');
}

构造函数的参数

构造函数的参数和普通函数参数一样,可以是位置参数、可选位置、命名参数等。

如下,使用命名参数对_name成员变量赋值。

class MyAdd {
  double a;
  double b;
  String? _name;

  MyAdd(this.a,this.b,{String? name}):_name=name;
  //...
}

使用方法:

var add1=MyAdd(1,2);
var add2=MyAdd(1,2,name:"add2");

和普通函数的调用一样。

类中的非空类型的成员变量,必须进行初始化,要么在声明时初始化,要么在构造函数中进行初始化。

命名参数是可选的,所以,要么是可空的类型,要么需要提供默认值,否则,就要使用required关键字,保证参数是必须的,实现成员的初始化。

class MyAdd {
  double a;
  double b;
  String _name;

  MyAdd(this.a,this.b,{String name="MyAdd"}):_name=name;
}

Dart中大量使用了命名参数的形式,将MyAdd构造函数改为命名参数。

class MyAdd {
  double a;
  double b;
  String? _name;

  MyAdd({
    this.a=0,
    required this.b,
    String? name
  }):_name=name;
  // ...
}

使用形式如下:

var add1=MyAdd(b:2);
var add2=MyAdd(a:10,b:15,name:"add2");

命名构造函数

在Dart中,上面所示的构造函数形式被称为“生成式构造函数”( generative constructor)。

使用命名式构造函数(Named constructor),可以为一个类声明多个构造函数,用于以不同形式创建对象。通过命名的方式,表达更明确的意图。

以一个表示向量的类为例:

import 'dart:math' as math;

class Vec2 {
  double x;
  double y;

  Vec2(this.x, this.y);

  Vec2.polar(double length, double rad)
      : x = length * math.cos(rad),
        y = length * math.sin(rad);
}

通常,都是使用Vec2(this.x, this.y)坐标系中的坐标来构建向量;

除此之外,还可以使用极坐标来获取,即命名构造函数Vec2.polar(double length, double rad),通过长度和角度来创建向量。

命名构造函数通过类名.构造名的形式定义,可以定义多个不同含义或使用场景命名的构造函数。

重定向构造函数

有时,类中的构造函数仅用于调用类中其它的构造函数,此时该构造函数没有函数体,在函数签名后使用:,通过this指定需要重定向的其他构造函数。

本质上,是构造函数的重新利用,避免写相关的重复代码。

class Vec2 {
  double x;
  double y;

  Vec2(this.x, this.y);

  Vec2.alongXAxis(double x) : this(x, 0);
}

成员变量(对象属性)的 Getter 和 Setter

类中的变量称为成员变量,也称为对象的属性,或,实例变量。通过对象可以访问和修改其属性。

比如MyAddab成员变量、Vec2xy成员变量。

除了像正常的变量一样定义和使用成员变量,还可以使用 GetterSetter 实现对象属性的读写方法。

默认,实例对象的每一个属性都有一个隐式的 Getter 方法,如果为非 final 属性的话还会有一个 Setter 方法。

所有未初始化的实例变量其值均为 null。

可以使用 get 和 set 关键字显式的定义 Getter 和 Setter 方法。get/set属性本质上是特殊的方法。

比如,为Vec2添加一个只读的length属性。

import 'dart:math' as math;

class Vec2 {
  double x;
  double y;

  Vec2(this.x, this.y);

  double get length => math.sqrt(x * x + y * y);
  // 等同
  // double get length{
  //   return math.sqrt(x * x + y * y);
  // }
}

通常 Getter 和 Setter 都写为箭头函数的形式。

下面以一个正方形的类为例,看一下get、set两者的具体使用:

class Rectangle {
  double left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // 定义两个计算属性: right 和 bottom.
  double get right => left + width;
  set right(double value) => left = value - width;
  
  double get bottom => top + height;
  set bottom(double value) => top = value - height;
}

get 关键字用于获取属性值,此方法不能传参;set 关键字用于向属性设置值,此方法只能传入一个参数(即属性赋值时=右边的值)。

只有get方法的属性称为只读属性;只有set方法的属性称为只写属性。

类变量和方法(静态成员)

使用static关键字可以声明静态成员,静态变量和静态方法是属于类的,而不是类创建的对象,因此,也被称为类变量和类方法。

静态成员由 类 本身直接访问和修改,不依附于 对象。

下面定义一个Person类,其中静态变量dynasty表示朝代。

class Person {
  String name;
  static String dynasty = "";

  Person(this.name);

  void info() {
    print('$name是$dynasty人');
  }
}

通过类名.静态成员名直接访问或修改静态成员。

Person.dynasty = "唐朝";
print(Person.dynasty); // 唐朝

普通成员方法可以直接访问静态成员,但是静态成员中不能访问实例方法或属性。

下面通过Person对象的info方法直接访问静态变量:

Person.dynasty = "唐朝";

var p = Person('李白');
p.info(); // 李白是唐朝人

静态成员是属于类的,实例对象共享同一个静态成员,通过类修改静态成员后,所有的对象都能访问到。

这是静态成员变量的特点,它不依赖于具体对象,是类本身的一种属性,是所有对象的公共属性或特性

下面,首先创建一些唐朝人,然后修改朝代,再创建一些宋朝人:

// 创建一些唐朝Person对象
Person.dynasty = "唐朝";
var p1 = Person('李白');
var p2 = Person('杜甫');
p1.info();
p2.info();


// 创建一些宋朝Person对象
Person.dynasty = "宋朝";
var p3 = Person('苏轼');
var p4 = Person('范仲淹');
p3.info();
p4.info();

// -------------------------
// 输出:
// 李白是唐朝人
// 杜甫是唐朝人
// 苏轼是宋朝人
// 范仲淹是宋朝人

static也可以修饰类的成员方法,和静态变量一样,静态方法也是属于类的,独立于具体的对象而存在。通过类名.静态方法名();进行调用。

比如下面的静态方法printDynasty,调用方式Person.printDynasty();

class Person {
  String name;
  static String dynasty = "";

  Person(this.name);

  void info() {
    print('$name是$dynasty人');
  }

  static void printDynasty() {
    print("现在朝代是: $dynasty");
  }
}

静态方法中不能访问实例变量或实例方法,因为实例成员属于对象,只用对象才能调用。而静态方法中是没有当前实例对象的。

如果在静态方法中访问实例成员将会报错。

类中的不可变成员

final关键字修饰的成员在声明后只能被赋值一次,在类中,通常用于构造函数中对其进行初始化赋值。此后,不允许被修改。

class Vec2 {
 final double x;
 final double y;

  Vec2(this.x, this.y);
}

修改final变量将会报错:

通常对于没有修改需求的成员变量使用final修饰,避免后续对其进行误操作,修改掉固定值。比如上面的Vec2对象,在创建后表示一个唯一的点,点对象创建后不允许修改。如果想要得到另一个点,需要创建一个新的Vec2对象。

const关键字用在类中,必须修饰静态成员,并且要在声明时初始化。

参考

相关文章
|
26天前
|
开发框架 Dart 前端开发
Flutter 是谷歌推出的一款高效跨平台移动应用开发框架,使用 Dart 语言,具备快速开发、跨平台支持、高性能、热重载及美观界面等特点。
Flutter 是谷歌推出的一款高效跨平台移动应用开发框架,使用 Dart 语言,具备快速开发、跨平台支持、高性能、热重载及美观界面等特点。本文从 Flutter 简介、特点、开发环境搭建、应用架构、组件详解、路由管理、状态管理、与原生代码交互、性能优化、应用发布与部署及未来趋势等方面,全面解析 Flutter 技术,助你掌握这一前沿开发工具。
56 8
|
3月前
|
Dart
如何在 Flutter 项目中使用 Dart 语言?
如何在 Flutter 项目中使用 Dart 语言?
137 58
|
1月前
|
Dart
flutter dart mixin 姿势
flutter dart mixin 姿势
|
2月前
|
Dart 开发者 Windows
flutter:dart的学习
本文介绍了Dart语言的下载方法及基本使用,包括在Windows系统上和VSCode中的安装步骤,并展示了如何运行Dart代码。此外,还详细说明了Dart的基础语法、构造函数、泛型以及库的使用方法。文中通过示例代码解释了闭包、运算符等概念,并介绍了Dart的新特性如非空断言操作符和延迟初始化变量。最后,提供了添加第三方库依赖的方法。
34 12
|
2月前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
91 7
|
存储 Dart
[Flutter]足够入门的Dart语言系列之常见运算符或操作符
Dart中的运算符提供对数据操作和处理的能力,其中的算术运算符、逻辑运算符非常符合现实中的使用情况,其他不同的运算符则有着自己的操作逻辑...
643 0
[Flutter]足够入门的Dart语言系列之常见运算符或操作符
|
存储 缓存 Dart
[Flutter]足够入门的Dart语言系列之面向对象:类成员的可见性、常量和工厂构造函数详解
类和成员的可见性,这在基于库或包的开发中非常重要,Dart中提供了工厂构造函数,可以方便的实现单例、缓存实例对象、返回子类等,以及常量构造函数的使用......
309 0
[Flutter]足够入门的Dart语言系列之面向对象:类成员的可见性、常量和工厂构造函数详解
|
Dart
[Flutter]足够入门的Dart语言系列之函数:函数定义、调用、5种参数类型和main函数
函数(Function)也被称为方法(Method)。其最直观的理解就是数据中的函数,比如y=f(x),在编程中,f对输入x进行处理,返回结果y,就是一个函数......
1061 0
[Flutter]足够入门的Dart语言系列之函数:函数定义、调用、5种参数类型和main函数
|
7月前
|
前端开发 C++ 容器
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
|
7月前
|
开发框架 Dart Java
Flutter的核心:Dart语言基础——语法与特性深度解析
【4月更文挑战第26天】Flutter框架背后的Dart语言,以其简洁的语法和独特特性深受开发者喜爱。本文深入解析Dart的语法与特性,如类型推导、动态静态类型系统、统一的类接口、访问权限控制以及并发编程支持。了解并掌握Dart,能助开发者更高效地利用Flutter构建高性能移动应用。