TypeScript:接口和多态

什么是接口

接口是一种自定义类型, 也就是类型了, 所以不但可以声明变量的类型,也可以声明函数参数(也是变量类型),函数返回值的类型

一、接口定义函数变量类型

//函数类型接口
interface addType {
    (bValue: number, secValue: number):number
}
function add(x: number, y: number): number {
    return x + y;
}
let myadd1:addType = add;
​
let myadd2:addType = (x:number, y:number):number=>{
    return x+y;
}

 

二、接口与对象

(1)对象是字面量对象, 对象中成员必须与接口中声明的成员完全相同,不可多,也不可少

interface IPerson { 
    firstName:string, 
    lastName:string, 
    sayHi: ()=>string ,
    [prop:string]:any;//你可以随便扩展    值是任意类型   的   任意属性
} 
​
let teacher:IPerson = { 
    firstName:"Tom",
    lastName:"Hanks", 
    sayHi: ():string =>{return "Hi there"} 
} 
console.log(teacher);
​

以下代码也是对的
let teacher: { 
    firstName:string, 
    lastName:string, 
    sayHi: ()=>string 
} = { 
    firstName:"Tom",
    lastName:"Hanks", 
    sayHi: ():string =>{return "Hi there"} 
} 

 

(2)对象是通过new 出的对象, 对象中成员必须包含接口中声明的成员不可少,但是可以增加接口中未声明的成员

interface IPerson { 
    firstName:string, 
    lastName:string, 
    sayHi: ()=>string 
} 
​
class Teacher {
    firstName:string="小五";
    lastName:string="小二";
    age:number= 30; //接口中没有声明,但是不会错
    sayHi():string {
        return "Hi there";
    } 
}
​
let teacher:IPerson = new Teacher();
console.log(teacher);
​

 


以下代码也是对的
let teacher:{ 
    firstName:string, 
    lastName:string, 
    sayHi: ()=>string 
}  = new Teacher();

 

 

(3)类型断言情况下。对象中成员只能是接口中声明过的成员,但是可少

对于下面示例中stu对象中的成员可以包含firstName,lastName,sayHI, 可以少,不能出现接口中没有出现过的

interface IPerson { 
    firstName:string, 
    lastName:string, 
    sayHi: ()=>string 
} 
let stu:IPerson = <IPerson> {};  
​
​
stu.firstName = "司空";
stu.sayHi = ():string =>{return "Hi there"} 
console.log(stu);
//stu.xx = "dfds"//错,因为接口中没有出现过

 

三、接口与数组

接口可以规定数组中元素的索引类型和元素类型,先看一个与默认情况相同的示例

//规定以number为元素索引、string为元素类型
interface Inamelist { 
    [index:number]: string
} 
​
let list: string[] = ["John","Bran"] 
list[2] = "jeep";
console.log(list);
​
console.log(list[0]);
console.log(list[1]);
console.log(list[2]);

 

也可以使用非number类型为元素索引

//规定以string为元素索引、string为元素类型
interface Inamelist {
    [index: string]: string
}
let list: Inamelist = [] as unknown as Inamelist;
list['a'] = "好的";
list['b'] = "不错的";
console.log(list);
console.log(list['a']);//好的
console.log(list["b"]);//不错的

 

四、接口继承接口

接口继承接口后,子接口就除了自身接口声明之外还包含了父接口声明

interface Person { 
   age:number 
} 
 
interface Musician extends Person { 
   instrument:string 
} 

 

接口继承接口是可以多继承的

interface IParent1 {
    v1:number 
} 
interface IParent2 {
    v2:number 
} 
//Iparent1和Iparent2顺序是无关仅要的
interface Child extends IParent1, IParent2 { 
    v3:string
} 

 

五、类实现接口

类可以实现接口,并可以多实现

类实现接口后,类体中必须实现接口所有字段和方法,当然类可以扩展自己本质的成员

interface IShape { 
   draw():void; 
   size:number;
}
interface IName {
    name: string;
}
class Circle implements IShape, IName { 
   public size:number = 30;
   public name:string = "圆";
   public draw():void { 
      console.log("会绘制一个"+this.name); 
   } 
   public show() {
       console.log("本来是Circle");
   }
}
let c:Circle = new Circle();
c.draw();
c.size = 31;
c.name = "四边型";
c.show();

 

类可以继承父类的同时实现接口

interface IShape { 
   draw():void; 
   size:number;
}
interface IName {
    name: string;
}
class Shape {
    public showArea():void {
        console.log("面积是?平方")
    }
}
class Circle extends Shape implements IShape, IName { 
   public size:number = 30;
   public name:string = "圆";
   public draw():void { 
      console.log("会绘制一个"+this.name); 
   } 
   public show() {
       console.log("本来是Circle");
   }
}
let c:Circle = new Circle();
c.draw();
c.size = 31;
c.name = "四边型";
c.showArea();
c.show();

 

六、 泛型函数类型接口(见泛型章节 )

//泛型函数类型接口1
interface GenericIdentityFn1 {
    (arg: T): T;
}
//泛型类函数型接口2
interface GenericIdentityFn2<T> {
    (arg: T): T;
}

 

七、接口与泛型

 

八、接口变量指向实现类对象

只讲语法,关于接口变量指向实现类对象的核心思,见后面多态相关单节

接口变量可以引用接口实现类对象, 但是只能访问接口中声明过的成员, 接口变量也可赋值为null或undefined,

如果想使用此接口变量访问所有对象成员,那么可以对接口变量进行"类型断言"为对象所属类的类型。

interface IShape {
    draw():void; 
    size:number;
 }
 interface IName {
     name: string;
 }
 class Circle implements IShape, IName {
    size:number = 30;
    name:string = "圆";
    draw():void { 
       console.log("会绘制一个"+this.name); 
    } 
    show() {
        console.log("本来是Circle");
    }
 }
 let c:IShape = new Circle();
 c.draw();//对,因为IShape中出现过
 c.size = 31;//对,因为IShape中出现过
 //c.name = "四边型";//错
 //c.show();//错
 let c2 = <Circle>c;//类型断言
 c2.name = "四边型";//
 c2.show();//

 

九、多态

面向对象核心思想是继承、封装与多态, 本小节只讲多态

1 概念

  • 多态概念: 一种状态多种表现形式

  • 方法中this在编译时并不知道是哪个对象,执行过程中动态确定, 因为是在执行过程中确定的this,所以this属于运行时多态。

  • "接口变量指向实现类对象"或"父类指向子类对象+重写",可以实现运行时多态。

  • 有了接口变量引用对象,或父类变量引用子类对象,可以非常清晰的代码实现动态代理设计模式。

2 接口变量实现多态

下面示例, 在myshow方法中的 "a.show()"在编译的时候并不确定是执行X中的show()还是执行Y中的show(),还是其它类中的show, 只有运行过程中才能确定,完美实现动态多态性

interface A {
    show():void;
}
class X implements A {
    show() {
        console.log("这是X的show()")
    }
}
class Y implements A {
    show() {
        console.log("这是Y的show()")
    }
}
class Test {
    myshow(a: A) { //a=new X()  a=new Y()
        a.show();
    }
}
let x = new X();
let y = new Y();
let t = new Test();
t.myshow(x);//这是X的show()
t.myshow(y);//这是Y的show()

 

3 父类变量+重写实现多态

下面示例中, 同样 myshow方法中的 "a.show()" 在运行过程中才知道是调用哪个类中的show()

class A {
    show() {
        console.log("A中的show")
    }
}
class X extends A {
    show() {
        super.show()
        console.log("这是X的show()")
    }
}
class Y extends A {
    show() {
        super.show();
        console.log("这是Y的show()")
    }
}
class Test {
    myshow(a: A) {//a=new A() a=new X(); a=new Y();
        a.show();
    }
}
let y = new Y();
let t = new Test();
t.myshow(new X());//这是X的show()
t.myshow(new Y());//这是Y的show()
t.myshow(new A());//主是A的show()
 

 

 

posted on 2022-09-26 19:56  香香鲲  阅读(344)  评论(0编辑  收藏  举报