ArkTS语言入门之接口、泛型、空安全、特殊运算符等

打印 上一主题 下一主题

主题 1562|帖子 1562|积分 4686

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
前言

臭宝们,今天我们来学习ArkTS中最后的一些内容。
实现接口

包罗implements子句的类必须实现列出的接口中定义的全部方法,但使用默认实现定义的方法除外。
  1. interface DateInterface {
  2.   now(): string;
  3. }
  4. class MyDate implements DateInterface {
  5.   now(): string {
  6.     // 在此实现
  7.     return 'now';
  8.   }
  9. }
复制代码
接口属性

接口属性可以是字段、getter、setter或getter和setter组合的形式。
  1. interface Style {
  2.   color: string;
  3. }
复制代码
接口继承

接口可以继承其他接口,如下面的示例所示:
  1. interface Style {
  2.   color: string;
  3. }
  4. interface ExtendedStyle extends Style {  width: number;}
复制代码
注意:继承接口包罗被继承接口的全部属性和方法,还可以添加本身的属性和方法。
抽象类和接口

在上一节中,我们先容了如安在ArkTS中使用抽象类。抽象类与接口都无法实例化。抽象类是类的抽象,抽象类用来捕捉子类的通用特性,接口是行为的抽象。在ArkTS中抽象类与接口的区别如下:


  • 一个类只能继承一个抽象类,而一个类可以实现一个或多个接口;
  • 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
  • 抽象类里面可以有方法的实现,但是接口完全都是抽象的,不存在方法的实现;
  • 抽象类可以有构造函数,而接口不能有构造函数。
泛型在接口和类中的应用

  1. class CustomStack<Element> {
  2.   public push(e: Element):void {
  3.     // ...
  4.   }
  5. }
复制代码
要使用类型CustomStack,必须为每个类型参数指定类型实参:
  1. let s = new CustomStack<string>();
  2. s.push('hello');
复制代码
编译器在使用泛型类型和函数时会确保类型安全。参见以下示例:
  1. let s = new CustomStack<string>();
  2. s.push(55); // 将会产生编译时错误
复制代码
泛型束缚

泛型类型的类型参数可以被限制只能取某些特定的值。例如,MyHashMap<Key, Value>这个类中的Key类型参数必须具有hash方法。
  1. interface Hashable {
  2.   hash(): number;
  3. }
  4. class MyHashMap<Key extends Hashable, Value> {
  5.   public set(k: Key, v: Value) {
  6.     let h = k.hash();
  7.     // ...其他代码...
  8.   }
  9. }
复制代码
在上面的例子中,Key类型扩展了Hashable,Hashable接口的全部方法都可以为key调用。
泛型函数

  1. function last(x: number[]): number {
  2.   return x[x.length - 1];
  3. }
  4. last([1, 2, 3]); // 3
复制代码
假如需要为任何数组定义相同的函数,使用类型参数将该函数定义为泛型:
  1. function last<T>(x: T[]): T {
  2.   return x[x.length - 1];
  3. }
复制代码
如今,该函数可以与任何数组一起使用。
在函数调用中,类型实参可以显式或隐式设置:
  1. // 显式设置的类型实参
  2. last<string>(['aa', 'bb']);
  3. last<number>([1, 2, 3]);
  4. // 隐式设置的类型实参
  5. // 编译器根据调用参数的类型来确定类型实参
  6. last([1, 2, 3]);
  7. last(['aa', 'bb']);
复制代码
泛型默认值

泛型类型的类型参数可以设置默认值。这样可以不指定实际的类型实参,而只使用泛型类型名称。下面的示例展示了类和函数的这一点。
  1. class SomeType {}
  2. interface Interface <T1 = SomeType> { }
  3. class Base <T2 = SomeType> { }
  4. class Derived1 extends Base implements Interface { }
  5. // Derived1在语义上等价于Derived2
  6. class Derived2 extends Base<SomeType> implements Interface<SomeType> { }
  7. function foo<T = number>(): T {
  8.   // ...
  9. }
  10. foo();
  11. // 此函数在语义上等价于下面的调用
  12. foo<number>();
复制代码
空安全

在ArkTS中,ArkTS中的全部类型都是不可为空的,在下面的示例中,全部行都会导致编译时错误:
  1. let x: number = null;    // 编译时错误
  2. let y: string = null;    // 编译时错误
  3. let z: number[] = null;  // 编译时错误
复制代码
可以为空值的变量定义为连合类型T | null。
  1. let x: number | null = null;
  2. x = 1;    // ok
  3. x = null; // ok
  4. if (x != null) { /* do something */ }
复制代码
非空断言运算符(!)

后缀运算符!可用于断言其使用数为非空。
应用于可空类型的值时,它的编译时类型变为非空类型。例如,类型将从T | null更改为T:
  1. class A {
  2.   value: number = 0;
  3. }
  4. function foo(a: A | null) {
  5.   a.value;   // 编译时错误:无法访问可空值的属性
  6.   a!.value;  // 编译通过,如果运行时a的值非空,可以访问到a的属性;如果运行时a的值为空,则发生运行时异常
  7. }
复制代码
空值归并运算符(??)

空值归并二元运算符??用于检查左侧表达式的求值是否即是null或者undefined。假如是,则表达式的效果为右侧表达式;否则,效果为左侧表达式。
换句话说,a ?? b等价于三元运算符(a != null && a != undefined) ? a : b。
在以下示例中,getNick方法假如设置了昵称,则返回昵称;否则,返回空字符串:
  1. class Person {
  2.   // ...
  3.   nick: string | null = null;
  4.   getNick(): string {
  5.     return this.nick ?? '';
  6.   }
  7. }
复制代码
可选链

在访问对象属性时,假如该属性是undefined或者null,可选链运算符会返回undefined。
  1. class Person {
  2.   nick: string | null = null;
  3.   spouse?: Person
  4.   setSpouse(spouse: Person): void {
  5.     this.spouse = spouse;
  6.   }
  7.   getSpouseNick(): string | null | undefined {
  8.     return this.spouse?.nick;
  9.   }
  10.   constructor(nick: string) {
  11.     this.nick = nick;
  12.     this.spouse = undefined;
  13.   }
  14. }
复制代码
模块

步伐可划分为多组编译单元或模块。
每个模块都有其本身的作用域,即,在模块中创建的任何声明(变量、函数、类等)在该模块之外都不可见,除非它们被显式导出。
与此相对,从另一个模块导出的变量、函数、类、接口等必须首先导入到模块中。
导出

可以使用关键字export导出顶层的声明。
未导出的声明名称被视为私著名称,只能在声明该名称的模块中使用。
  1. export class Point {
  2.   x: number = 0;
  3.   y: number = 0;
  4.   constructor(x: number, y: number) {
  5.     this.x = x;
  6.     this.y = y;
  7.   }
  8. }
  9. export let Origin = new Point(0, 0);
  10. export function Distance(p1: Point, p2: Point): number {
  11.   return Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
  12. }
复制代码
导入

导入声明用于导入从其他模块导出的实体,并在当前模块中提供其绑定。导入声明由两部分组成:


  • 导入路径,用于指定导入的模块;
  • 导入绑定,用于定义导入的模块中的可用实体集和使用形式(限定或不限定使用)。
假设模块具有路径“./utils”和导出实体“X”和“Y”。
导入绑定* as A表现绑定名称“A”,通过A.name可访问从导入路径指定的模块导出的全部实体:导入绑定可以有几种形式。
  1. import * as Utils from './utils';
  2. Utils.X // 表示来自Utils的X
  3. Utils.Y // 表示来自Utils的Y
复制代码
导入绑定{ ident1, …, identN }表现将导出的实体与指定名称绑定,该名称可以用作简朴名称:
  1. import { X, Y } from './utils';
  2. X // 表示来自utils的X
  3. Y // 表示来自utils的Y
复制代码
假如标识符列表定义了ident as alias,则实体ident将绑定在名称alias下:
  1. import { X as Z, Y } from './utils';
  2. Z // 表示来自Utils的X
  3. Y // 表示来自Utils的Y
  4. X // 编译时错误:'X'不可见
复制代码
动态导入

应用开发的有些场景中,假如希望根据条件导入模块或者按需导入模块,可以使用动态导入代替静态导入。
import()语法通常称为动态导入(dynamic import),是一种类似函数的表达式,用来动态导入模块。以这种方式调用,将返回一个promise。
如下例所示,import(modulePath)可以加载模块并返回一个promise,该promise resolve为一个包罗其全部导出的模块对象。该表达式可以在代码中的恣意位置调用。
  1. // Calc.ts
  2. export function add(a:number, b:number):number {
  3.   let c = a + b;
  4.   console.info('Dynamic import, %d + %d = %d', a, b, c);
  5.   return c;
  6. }
  7. // Index.ts
  8. import("./Calc").then((obj: ESObject) => {
  9.   console.info(obj.add(3, 5));  
  10. }).catch((err: Error) => {
  11.   console.error("Module dynamic import error: ", err);
  12. });
复制代码
假如在异步函数中,可以使用let module = await import(modulePath)。
  1. // say.ts
  2. export function hi() {
  3.   console.log('Hello');
  4. }
  5. export function bye() {
  6.   console.log('Bye');
  7. }
复制代码
  1. async function test() {
  2.   let ns = await import('./say');
  3.   let hi = ns.hi;
  4.   let bye = ns.bye;
  5.   hi();
  6.   bye();
  7. }
复制代码
this 关键字

关键字this只能在类的实例方法中使用。
  1. class A {
  2.   count: string = 'a';
  3.   m(i: string): void {
  4.     this.count = i;
  5.   }
  6. }
复制代码
使用限制:


  • 不支持this类型
  • 不支持在函数和类的静态方法中使用this
  1. class A {
  2.   n: number = 0;
  3.   f1(arg1: this) {} // 编译时错误,不支持this类型
  4.   static f2(arg1: number) {
  5.     this.n = arg1;  // 编译时错误,不支持在类的静态方法中使用this
  6.   }
  7. }
  8. function foo(arg1: number) {
  9.   this.n = i;       // 编译时错误,不支持在函数中使用this
  10. }
复制代码
关键字this的指向:


  • 调用实例方法的对象
  • 正在构造的对象
末端

至此,我们已经学习了ArkTS的根本语法。下一步,我们将学习ArkUI框架。臭宝们,冲鸭!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农民

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表