ArkTS基础语法 |(3)类和接口

ArkTS基础语法 |(3)类和接口

在学习HarmonyOS开发的核心语言ArkTS时,整理了一份基础语法笔记,方便日后回顾。

一、类的基础定义与实例化

类是ArkTS面向对象编程的核心载体,用于封装数据(字段)和行为(方法)。

类声明会引入一个新类型,并定义其字段、方法和构造函数,核心类成员包含实例字段实例方法构造函数

定义类后,可通过关键字new对象字面量两种方式创建类的实例。

new 适合带构造函数的类,对象字面量适合无自定义构造函数的简单类。

1. 关键字 new 创建实例(带构造函数)
TypeScript 复制代码
// 定义包含构造函数、实例字段、实例方法的Person类
class Person {
  name: string = '';   // 实例字段 显式初始化
  surname: string = '';   // 实例字段 显式初始化
  // 构造函数:初始化实例字段
  constructor (n: string, sn: string) {
    this.name = n;
    this.surname = sn;
  }
  // 实例方法:拼接姓名
  fullName(): string {
    return this.name + ' ' + this.surname;
  }
}
// new关键字创建实例并调用方法
let p = new Person('John', 'Smith');
console.info(p.fullName());   // 输出:John Smith
2. 对象字面量创建实例(无自定义构造函数)
TypeScript 复制代码
// 定义简单的Point类 仅包含实例字段
class Point {
  x: number = 0;
  y: number = 0;
}
// 对象字面量创建实例 直接赋值字段
let p: Point = {x: 42, y: 42};

注意 :对象字面量仅能在类型可推导的上下文中使用,且赋值的字段需与类的字段完全匹配。

二、类的字段

字段是类中声明的变量,用于存储数据,ArkTS中将类的字段分为实例字段静态字段 ,同时要求所有字段必须显式初始化 ,且支持通过getter/setter实现属性的受控访问。

1. 实例字段与静态字段
实例字段
  • 属于类的每个实例,每个实例拥有独立的实例字段集合。

  • 必须通过类的实例访问,不能通过类名直接访问。

  • 声明时需显式初始化,或在构造函数中完成初始化。

静态字段
  • 使用关键字static 声明,属于类本身,所有实例共享同一个静态字段。

  • 必须通过类名访问,不能通过实例访问。

  • 声明时需显式初始化。

TypeScript 复制代码
// 示例:实例字段与静态字段的使用
class Student {
  // 实例字段:每个学生的独立姓名
  name: string = '';
  // 静态字段:所有学生的共同学校(共享值)
  static school: string = 'HarmonyOS大学';
  constructor(n: string) {
    this.name = n;
  }
}
// 创建两个实例
let s1 = new Student('小李');
let s2 = new Student('小何');

// 通过实例访问实例字段
console.log(s1.name);   // 输出:小李
// 通过类名访问静态字段
console.log(Student.school);   // 输出:HarmonyOS大学

// 修改静态字段 所有实例共享修改后的值
Student.school = '鸿蒙大学';
console.log(Student.school);   // 输出:鸿蒙大学
2. 字段初始化规则

ArkTS要求所有字段必须在声明时或构造函数中显式初始化 (与标准TypeScript的strictPropertyInitialization模式一致),未初始化的字段会导致编译错误 ,该规则可减少运行时错误,提升程序执行性能。

3. getter和setter

getter/setter用于对类的属性进行受控访问,可在属性赋值/获取时增加逻辑校验,替代直接的字段访问。

在类中可单独定义getter、单独定义setter,或二者组合定义。

TypeScript 复制代码
// 示例:getter/setter的使用
class Circle {
  private _radius: number = 0;   // 私有字段 仅类内部可访问
  // getter:获取半径 增加非负校验
  get radius(): number {
    return this._radius < 0 ? 0 : this._radius;
  }
  // setter:设置半径 过滤负数
  set radius(r: number) {
    this._radius = r >= 0 ? r : 0;
  }
}
let c = new Circle();
c.radius = -5;   // 赋值负数 被setter过滤
console.log(c.radius);   // 输出:0
c.radius = 10;
console.log(c.radius);   // 输出:10

三、类的方法

方法是类中封装的行为逻辑,ArkTS中将类的方法分为实例方法静态方法 ,二者的访问范围、调用方式存在明显区别。

1. 实例方法
  • 属于类的实例,必须通过类的实例调用。

  • 可访问实例字段静态字段,包括类的私有字段。

  • 是类最常用的方法类型,用于实现实例的具体行为。

TypeScript 复制代码
// 示例:实例方法计算矩形面积
class RectangleSize {
  // 私有实例字段 仅类内部可访问
  private height: number = 0;
  private width: number = 0;
  // 构造函数初始化私有字段
  constructor(height: number, width: number) {
    this.height = height;
    this.width = width;
  }
  // 实例方法:计算面积 可访问私有字段
  calculateArea(): number {
    return this.height * this.width;
  }
}
// 实例化后调用实例方法
let square = new RectangleSize(10, 10);
console.log(square.calculateArea());   // 输出:100
2. 静态方法
  • 使用关键字static 声明,属于类本身 ,必须通过类名调用。

  • 仅能访问静态字段/其他静态方法,无法访问实例字段(因无具体实例)。

  • 用于实现类的公共行为,与实例无关。

TypeScript 复制代码
// 示例:静态方法的定义与调用
class Tool {
  // 静态方法:实现通用的字符串拼接逻辑
  static concatStr(a: string, b: string): string {
    return a + '-' + b;
  }
}
// 通过类名直接调用静态方法
console.info(Tool.concatStr('ArkTS', 'HarmonyOS'));   // 输出:ArkTS-HarmonyOS
3. 方法重载签名

方法重载允许为同一个方法 定义多个不同的签名 (参数类型/个数不同),实现方法的多场景调用,需遵循重载签名在前、实现签名在后的规则,实现签名需兼容所有重载签名的参数类型。

注意 :若两个重载签名的名称和参数列表完全相同,会导致编译错误。

TypeScript 复制代码
// 示例:方法重载签名
class C {
  // 重载签名1:参数为number类型
  foo(x: number): void;
  // 重载签名2:参数为string类型
  foo(x: string): void;
  // 实现签名:兼容number | string类型,编写具体逻辑
  foo(x: number | string): void {
    console.log('参数值:', x);
  }
}
let c = new C();
c.foo(123);     // 匹配重载签名1
c.foo('aa');    // 匹配重载签名2

四、类的继承

ArkTS支持单继承 ,即一个类只能继承一个基类(父类/超类),同时支持实现多个接口

子类(派生类)会继承父类的字段和方法(构造函数除外),并可新增字段/方法,或重写 父类的方法。

1. 继承的语法格式
TypeScript 复制代码
class 子类名 extends 父类名 implements 接口列表 {
  // 子类的字段、方法、构造函数
}
2. 核心特性

(1) 继承内容 :子类继承父类的实例字段、静态字段、实例方法、静态方法,不继承构造函数

(2) 方法重写 :子类可重写父类的方法,重写的方法必须与原方法参数类型一致,返回类型为原类型或其子类型。

(3) 父类访问 :通过关键字super访问父类的方法和构造函数,子类构造函数的第一条语句必须是super()(调用父类构造函数)。

(4) 接口实现 :包含implements子句的类,必须实现接口中所有未定义默认实现的方法。

3. 关键示例
(1)子类继承与super调用
TypeScript 复制代码
// 父类:矩形
class RectangleSize {
  width: number = 0;
  height: number = 0;
  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }
  // 父类方法:计算面积
  calculateArea(): number {
    return this.width * this.height;
  }
}
// 子类:正方形 继承自矩形
class Square extends RectangleSize {
  // 子类构造函数:必须先调用super()
  constructor(side: number) {
    super(side, side);   // 调用父类构造函数
  }
}
// 子类实例化 继承父类方法
let square = new Square(5);
console.log(square.calculateArea());   // 输出:25
(2)方法重写
TypeScript 复制代码
class Animal {
  name: string = '';
  constructor(n: string) {
    this.name = n;
  }
  // 父类方法
  say(): string {
    return '动物的叫声';
  }
}
// 子类:狗 重写父类say方法
class Dog extends Animal {
  constructor(n: string) {
    super(n);
  }
  // 方法重写:参数类型一致\返回类型一致
  say(): string {
    return this.name + ':汪汪汪';
  }
}
let dog = new Dog('旺财');
console.log(dog.say());   // 输出:旺财:汪汪汪

五、构造函数

构造函数用于初始化类的实例状态,是创建实例时自动执行的方法。

1. 基础语法
TypeScript 复制代码
constructor ([参数列表]) {
  // 初始化逻辑,通常为实例字段赋值
}
2. 核心规则

(1) 默认构造函数 :若类未显式定义构造函数,编译器会自动创建空参数的默认构造函数,使用字段类型的默认值初始化实例。

(2) 子类构造函数 :子类必须通过super()显式调用父类的构造函数,且super()必须是子类构造函数体的第一条语句

(3) 显式初始化:构造函数是实例字段初始化的重要位置,未在声明时初始化的字段,必须在构造函数中完成。

(4) 无返回值 :构造函数无需指定返回值类型,也不会返回任何值。

3. 构造函数重载签名

与方法重载类似,构造函数也支持重载,通过多个不同的重载签名指定构造函数的不同调用方式,实现签名需兼容所有重载签名

TypeScript 复制代码
// 示例:构造函数重载
class C {
  // 重载签名1:参数为number类型
  constructor(x: number);
  // 重载签名2:参数为string类型
  constructor(x: string);
  // 实现签名:兼容number | string类型
  constructor(x: number | string) {
    console.log('构造函数参数:', x);
  }
}
// 匹配不同的重载签名创建实例
let c1 = new C(123);     // 重载签名1
let c2 = new C('abc');   // 重载签名2

六、可见性修饰符

ArkTS为类的字段和方法提供了可见性修饰符 ,用于控制类成员的访问范围,实现封装性。 修饰符包括privateprotectedpublic默认可见性为public

1. 公有(public)
  • 最宽松的修饰符,默认所有成员均为public

  • public修饰的成员,在程序任何可访问该类的地方都能访问(类内部、实例、子类)。

2. 私有(private)
  • 最严格的修饰符。

  • private修饰的成员,仅能在声明该成员的类内部访问,类外部、子类均无法访问。

3. 受保护(protected)
  • 介于publicprivate之间。

  • protected修饰的成员,类内部和子类中可访问,类外部无法访问。

4. 完整示例
TypeScript 复制代码
// 父类
class Base {
  public a: string = '';      // 公有
  private b: string = '';     // 私有
  protected c: string = '';   // 受保护
  constructor() {
    this.a = 'public';
    this.b = 'private';
    this.c = 'protected';
  }
  // 类内部可访问所有修饰符的成员
  showBase() {
    console.log(this.a, this.b, this.c);
  }
}
// 子类
class Derived extends Base {
  showDerived() {
    console.log(this.a);   // public可访问
    // console.log(this.b);   // 编译错误,private子类不可访问
    console.log(this.c);   // protected子类可访问
  }
}
// 实例化
let base = new Base();
let derived = new Derived();

// 类外部访问
console.log(base.a);   // public可访问
// console.log(base.b);   // 编译错误 private外部不可访问
// console.log(base.c);   // 编译错误 protected外部不可访问

七、对象字面量

对象字面量是创建类实例的便捷方式,通过 {属性名: 值} 的形式直接初始化实例,同时也可用于初始化泛型Record类型 ,是ArkTS中创建对象的常用语法。

1. 类实例的对象字面量
TypeScript 复制代码
// 定义简单的Point类 仅包含实例字段
class Point {
  x: number = 0;
  y: number = 0;
}
// 对象字面量创建实例 直接赋值字段
let p: Point = {x: 42, y: 42};

核心要求是字段与类完全匹配类型可推导

2. Record类型的对象字面量

泛型Record<K, V>用于将键类型K 映射到值类型V ,常通过对象字面量初始化,其中K仅支持字符串类型数值类型 (不包括BigInt),V可为任意类型。

TypeScript 复制代码
// 示例:Record类型的对象字面量

// 定义Record类型:键为string 值为number
let userAge: Record<string, number> = {
  'John': 25,
  'Mary': 21,
  'Tom': 23
};
// 访问值
console.log(userAge['John']);   // 输出:25
console.log(userAge.Mary);      // 输出:21

// 键为数值类型的Record
let score: Record<number, string> = {
  100: '优秀',
  80: '良好',
  60: '及格'
};
console.log(score[80]);   // 输出:良好

八、抽象类

带有abstract 修饰符的类称为抽象类,抽象类是对一组具体类的通用特性抽象 ,无法直接实例化,主要用于作为父类被子类继承,同时支持定义抽象方法

1. 核心特性

(1)不可实例化 :直接通过new创建抽象类实例会导致编译错误。

(2)可被继承 :抽象类的子类可以是抽象类,也可以是非抽象类,非抽象子类可实例化

(3)可包含普通成员:抽象类中可定义普通的实例字段、静态字段、实例方法、静态方法。

(4)可包含构造函数 :抽象类的构造函数可用于初始化子类的公共字段,子类通过super()调用。

2. 抽象方法

带有abstract 修饰符的方法称为抽象方法,仅能在抽象类中声明

核心特性:

(1)仅声明,无实现:抽象方法只有方法签名,没有方法体。

(2)子类必须实现:抽象类的非抽象子类,必须实现父类中所有的抽象方法。

(3)不可单独存在 :非抽象类中声明抽象方法会导致编译错误。

3. 完整示例
TypeScript 复制代码
// 抽象类:形状
abstract class Shape {
  color: string = '';
  // 抽象类的构造函数
  constructor(c: string) {
    this.color = c;
  }
  // 普通方法:已有实现
  showColor(): void {
    console.log('形状颜色:', this.color);
  }
  // 抽象方法:仅声明 无实现 子类必须重写
  abstract calculateArea(): number;
}
// 非抽象子类:圆形 继承抽象类
class Circle extends Shape {
  radius: number = 0;
  constructor(c: string, r: number) {
    super(c);   // 调用抽象类构造函数
    this.radius = r;
  }
  // 必须实现父类的抽象方法
  calculateArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}
// 实例化子类(抽象类无法实例化)
let circle = new Circle('red', 5);
circle.showColor();   // 输出:形状颜色:red
console.log(circle.calculateArea());   // 输出:78.53981633974483

// let shape = new Shape('blue');   // 编译错误:抽象类无法创建实例

// 编译错误:非抽象类声明抽象方法
// class Test {
//   abstract test(): void;
// }

九、接口

接口通过interface关键字声明,是代码协定的定义方式 ,用于约定类的属性和方法,实现多态特性

任何类只要实现了接口的所有成员,就可被视为该接口的类型。

ArkTS中接口支持继承约定对象结构约定对象方法 等特性。

1. 接口的基础定义

接口通常包含属性声明方法声明,无具体实现,仅定义"规范",实现接口的类必须严格遵循该规范。

TypeScript 复制代码
// 示例:基础接口定义

// 接口1:约定颜色属性
interface Style {
  color: string;   // 属性声明
}
// 接口2:约定方法
interface AreaSize {
  calculateAreaSize(): number;   // 方法声明
  someMethod(): void;            // 方法声明
}
2. 接口的属性声明

接口的属性可以是普通字段gettersetter ,或getter+setter组合。

其中普通字段是getter/setter对的便捷写法,二者完全等价。

TypeScript 复制代码
// 示例:接口属性的两种等价声明

// 方式1:普通字段形式(便捷写法)
interface Style {
  color: string;
}

// 方式2:getter+setter形式
interface Style {
  get color(): string;
  set color(x: string);
}

// 类实现接口的两种方式
class StyledRectangle implements Style {
  // 方式1:直接声明字段
  color: string = '';
}
class StyledCircle implements Style {
  // 方式2:通过getter/setter实现
  private _color: string = '';
  get color(): string { return this._color; }
  set color(x: string) { this._color = x; }
}
3. 接口继承

接口支持多继承 ,即一个接口可以继承多个其他接口,继承后的接口会包含被继承接口的所有成员,并可新增自己的成员。

TypeScript 复制代码
// 示例:接口继承

// 父接口1
interface Style {
  color: string;
}
// 父接口2
interface Size {
  width: number;
  height: number;
}
// 子接口:继承Style和Size 新增border属性
interface StyledSize extends Style, Size {
  border: number;
}
// 实现子接口的类 需实现所有父接口+子接口的成员
class Rectangle implements StyledSize {
  color: string = '';
  width: number = 0;
  height: number = 0;
  border: number = 0;
}
4. 接口约定对象结构与方法

ArkTS中通过接口严格约定对象的结构方法类型,是定义自定义对象类型的核心方式,适用于非类实例的普通对象。

核心规则:

  • 接口内属性之间不加逗号 ,对象内属性之间加逗号
  • 对象的属性/方法必须与接口一一对应,不可多也不可少。
(1)接口定义对象结构
TypeScript 复制代码
// 定义接口:约定User对象的属性名和类型(属性间无逗号)
interface User {
  name: string
  age: number
  isVip: boolean
}

// 基于接口创建对象:属性与接口一一对应(属性间加逗号)
let user1: User = {
  name: '小鸣',
  age: 20,
  isVip: true
};

// 访问对象属性
console.log(user1.name);   // 输出:小鸣
console.log(user1.age);   // 输出:20
(2)接口约定对象方法

对象方法用于描述对象的行为,接口中通过箭头函数约定方法的参数类型和返回值类型,实现对象时需严格遵循该约定。

  • 无参数方法:方法名: () => 返回值类型

  • 无返回值方法:返回值类型为void

  • 带参数方法:方法名: (参数1:类型1, 参数2:类型2) => 返回值类型

TypeScript 复制代码
// 示例:接口约定对象方法

// 定义接口:约定属性和方法
interface User {
  name: string
  // 无参数、无返回值方法
  sayName: () => void
  // 带1个string参数 返回string类型方法
  getAgeDesc: (prefix: string) => string
  // 带2个参数 返回number类型方法
  sum: (a: number, b: number) => number
}

// 基于接口实现对象
let user2: User = {
  name: '小何',
  // 实现无参数方法
  sayName: () => {
    console.log('我的名字是' + user2.name);
  },
  // 实现带参数方法
  getAgeDesc: (prefix: string) => {
    return prefix + '20岁';
  },
  // 实现多参数方法
  sum: (a: number, b: number) => {
    return a + b;
  }
};

// 调用对象方法
user2.sayName();   // 输出:我的名字是小何
let desc = user2.getAgeDesc('年龄:');
console.log(desc);   // 输出:年龄:20岁
console.log(user2.sum(10, 20));   // 输出:30
5. 抽象类与接口的区别

抽象类和接口均无法直接实例化,且都用于实现代码的抽象和复用,但二者在设计目的、语法规则上有本质区别。

在ArkTS中,抽象类和接口的核心区别如下表:

特性 抽象类(abstract class) 接口(interface)
继承/实现规则 类仅能单继承一个抽象类 类可实现多个接口
成员实现 可包含方法的具体实现,也可包含抽象方法 无任何方法实现,仅声明方法/属性
静态成员 可包含静态字段、静态方法、静态代码块 不可包含任何静态成员
构造函数 可定义构造函数,子类通过super()调用 不可定义构造函数
设计目的 类的抽象,捕捉子类的通用特性 行为的抽象,定义代码协定
字段/属性 可声明实例字段并初始化 仅声明属性类型,不可初始化
相关推荐
Lee_xiaoming3 天前
ArkTS基础语法 |(2)函数
arkts
全栈探索者3 天前
useState 换个名字叫 @State,仅此而已
react·harmonyos·arkts·前端开发·deveco studio·状态管理·鸿蒙next
全栈探索者3 天前
useContext 退场,@Provide + @Consume 登场
react.js·harmonyos·arkts·状态管理·前端转型
Lee_xiaoming4 天前
ArkTS基础语法 | (1)基本知识
arkts
全栈探索者5 天前
列表渲染不用 map,用 ForEach!—— React 开发者的鸿蒙入门指南(第 4 期)
react.js·harmonyos·arkts·foreach·列表渲染
ITUnicorn6 天前
【HarmonyOS6】ArkTS 自定义组件封装实战:动画水杯组件
华为·harmonyos·arkts·鸿蒙·harmonyos6
全栈探索者6 天前
@Component + struct = 你的新函数组件——React 开发者的鸿蒙入门指南(第 2 期)
react·harmonyos·arkts·前端开发·deveco studio·鸿蒙next·函数组件
我讲个笑话你可别哭啊7 天前
鸿蒙ArkTS快速入门
前端·ts·arkts·鸿蒙·方舟开发框架
We....8 天前
鸿蒙与Java跨平台Socket通信实战
java·服务器·tcp/ip·arkts·鸿蒙