接口,隐式实现,显式实现介绍
接口
接口是一种设计约定,主要针对类,用于规定其需要实现或具有的成员,在一些语言中,比如 C#,接口也适用于结构。当目标符合接口的约定时,可认为目标实现了接口,一个类可以实现多个接口,一个接口可以被多个类实现。
选择接口还是抽象类?
你需要根据情况来决定是使用接口还是抽象类,这可以从接口和类看待问题的角度说起,类侧重于以事物为中心,大多数类都会代表一种现实或抽象的事物,而接口侧重于以作用为中心,他体现了某种特定的作用或效果,并不关注背后的事物。
比如,某个函数希望调用方传递的实参具有存储功能,此时选择抽象类会遇到问题,因为你的存储类可能继承自其他类,在多重继承不被允许的情况下,这成为了一个小麻烦。
类的多态性
想要了解抽象类,你可以查看方法重写一段。
在下面的 C# 代码中,函数Go
的参数movable
的类型为接口IMovable
,该接口表示一种可移动的效果。类Car
实现了接口IMovable
,因此,实例car
可作为实参传递给函数Go
。
using System.Numerics;
// 函数 Go,用来移动一个可移动的目标
void Go(IMovable movable)
{
// 如果速度为 0,则不需要移动
if (movable.Speed == Vector2.Zero) {
Console.WriteLine("当前速度为 0?抛锚了?");
return;
}
// 移动并显示位置
movable.Move();
Console.WriteLine($"移动后的位置:{movable.Speed}");
}
// 创建 Car 的实例
Car car = new()
{
// 设置移动速度
Speed = new Vector2(1.5f, 2.5f)
};
// 调用 Go 方法进行移动
Go(car);
移动后的位置:<1.5, 2.5>
接口的隐式实现
接口的隐式实现,并不要求将成员明确的声明为接口的实现,虽然该成员起到了这样的作用。当隐式实现接口所约定的成员时,这些成员一般可通过类的实例或接口的实例来访问。
在 C# 类Car
中,接口IMovable
的成员Speed
被隐式实现。
using System.Numerics;
// 接口 IMovable,表示可移动
interface IMovable
{
// …
// 属性 Speed,表示移动速度
public Vector2 Speed { get; set; }
// …
}
// 类 Car
class Car : IMovable
{
// …
// 隐式实现了 IMovable 接口的成员 Speed
public Vector2 Speed { get; set; }
}
接口的显式实现
接口的显式实现,要求类的成员明确声明为接口的实现,当显式实现接口所约定的成员时,这些成员一般只能通过接口实例访问。
为何需要显式实现?
通过显式实现,类可以明确针对某个接口的某个成员,这在两个或更多接口具有同名成员,而你希望为他们编写各自的实现时,是有效的。
下面,为类Car
实现另一个接口IEMailable
,该接口与IMovable
均声明了成员Location
,我们通过显式的方式分别实现了他们。
using System.Numerics;
// 接口 IMovable,表示可移动
interface IMovable
{
// 属性 Location,表示当前位置
public Vector2 Location { get; set; }
// …
}
// 接口 IEMailable,表示可投递电子邮件
interface IEMailable
{
// 属性 Location,表示电子邮件的地址
public string Location { get; set; }
}
// 类 Car
class Car : IMovable, IEMailable
{
// 显式实现了 IMovable 接口的成员 Location
Vector2 IMovable.Location { get; set; }
// 显式实现了 IEMailable 接口的成员 Location
// 好吧,这是预约该车的方式
string IEMailable.Location { get; set; } = string.Empty;
// …
}
接口的默认方法
接口的默认方法,是指接口声明的具有默认实现的方法,由于已经被实现,需要实现接口的目标可以忽略这些方法。
接口IMovable
声明的方法Move
具有默认实现,因此,Move
是接口的一个默认方法。
// 接口 IMovable,表示可移动
interface IMovable
{
// …
// 方法 Move,表示移动一次
public void Move()
{
// 默认实现为,按照速度调整当前位置
Location += Speed;
}
}
接口的继承
和类一样,接口也拥有继承这一特点,被继承的接口被称为基接口或父接口,而继承自基接口的接口,被称为派生接口或子接口。
接口的继承和类的继承之间的区别
类包含了具体实现,继承中的不同类对同一成员的实现是需要加以区分的,接口通常仅包含约定,继承中同一约定多次出现及其归属问题对接口作用的影响不大。
什么是接口的多重继承?
接口的多重继承是指派生接口从多个基接口继承的行为,与类的多重继承不同,接口的多重继承是被普遍支持的。因为多义性的问题,在接口中是不存在或被忽略的。
面向对象
想要详细了解类的继承,多重继承,你可以查看类的继承一段。
下面的 C# 接口IDrivable
多重继承自IMovable
和IRotatable
。
// 接口 IMovable,表示可旋转
interface IRotatable
{
// 属性 Angle,表示当前角度
public float Angle { get; set; }
// 属性 Speed,表示旋转速度
public float Speed { get; set; }
// 方法 Rotate,表示旋转一次
public void Rotate()
{
// 默认实现为,按照速度旋转当前角度
Angle += Speed;
}
}
// 接口 IMovable,表示可驾驶
interface IDrivable : IMovable, IRotatable { }