从广义上说,凡是一个类提供给外部使用的部分都可以被称为接口。但是在引入继承和抽象类之前,这个广义接口并没有太大的意义。广义接口的真正意义是在类的继承中体现多态的功能,这种接口又被称为抽象接口。
狭义接口是指特定的函数集合,一般是用Interface声明的,它表示一个方法集合,这个集合被称为一个命名接口。一个命名接口中的方法必须在一个类中实现后才能被使用。
一个类继承实现一个接口,称为这个类实现了该接口。一个接口可以被多个类实现,一个类也可以继承多个接口,这样就形成了一种灵活的接口调用方式,从而实现更加灵活和节省资源的多态。
接口是另一种“is a” 关系
继承被看作是一种“is a ”关系(即派生类型同时也是基类类型)。对接口的实现,是另一种形式的“is a”关系:
如果Professor类扩张自Person类,则教授(Professor)也是人(person);
如果Professor类实现ITeacher接口,则教授(Professor)同时也是教师(teacher)。
同样,如果A类实现接口X,则所有派生自A的类都实现同一个接口。例如,如果从Professor类派生出AdjunctProfessor类,如果Professor类实现了ITeacher接口,则副教授(AdjunctProfessor)也是教师。
public class Professor :ITeacher
{
//ITeacher所需要的所有方法将被本类实现....细节从略
}
public class AdunctProfessor:Professor
{
//ITeacher类所需要的所有方法,至少是从Professor继承的下来的那些方法都被本类实现...细节从略。
}
这很容易理解,因为AdjunctProfessor类将从Professor类继承ITeacher接口要求的所有方法,或者覆载这些方法;AdjunctProfessor类也将所有能力执行ITeacher接口的服务。
实现多个接口
举个例子,如果我们创建第二个接口,名为IAdministrator,它定义了一下方法头:
public interface IAdministrator
{
bool approveNewCourse(Course c);
bool hireProfessor(Professor p);
}
我们可以把Professor类声明为同时实现ITeacher和IAdministrator 接口,这样,这个类就得实现两个接口的所以方法。
public class Professor :ITeacher,IAdministrator
{
//Professor 类必须实现ITeacher接口要求的方法...细节从略
//Professor类必须实现IAdministrator接口要求的方法....细节从略
}
注意,如果一个类实现的两个或多个接口中的方法拥有相同的方法签名,则在实现类中只需要实现其中一个——该方法将同时满足所有接口的实现要求。
一个类实现了超过一个接口,它的对象就有能力扮演应用程序中的多种身份或角色;这样的对象可以为不同类型的引用变量所维系。根据上述定义,Professor既是ITeacher又是IAdministrator,下面的客户代码时可能存在的:
//实例化一个Professor对象,将其句柄放在一个类型为Professor的引用变量中。
Professor p = new Professor();
//然后声明两个引用变量,类型分别为Professor类实现的两个接口类型。
ITeacher t;
IAdministrator a;
t = p ; //把Professor 对象的句柄放在类型为Teaher的引用变量中,这是可以的,因为教授同时也是老师!
a=p; //把Professor对象的句柄放在类型为Administrator的引用变量中,这是可以的,因为教授可能同时也是行政人员!
可见,一个Professor对象可以被Professor、ITeaher和IAdministrator类型的引用变量所维系,因为教授是教师,教授也可能是行政人员。Professor对象拥有三种身份。
这个概念等同于作为一个人的你,被不同人当作不同的角色看待的情形:在经理眼里你是雇员,在父母眼里你是孩子,在孩子眼里你是父母,如此等等。
我们可以把一个对象当做Professor来要求....
//Department 是为Professor类定义的属性
p.Department =“Computer Science”;
或者当做为一个Teacher....
//AgreeToTeach是为ITeacher接口定义的方法....
t.AgreeToTeach(c); //注意p.AgreeToTeach(c);也同样可以工作......
或者当作一个行政人员.....
//ApproveNewCourse是为IAdministrator接口定义的方法.....
a.approveNewCourse(c); //注意p.approveNewCourse(c);也同样可以工作....
因为它身兼三职!
接口和实例化
接口不能被实例化,因为接口中没有构造函数。也就是说,如果把ITeacher定义为一个接口,则不能将它实例化:
ITeacher t =new ITeacher(); //不能这样做!编译器将报错
error CS0144: Cannot create an instance of the abstract class or interface 'ITeacher'
虽然我们不能实例化一个接口,但却能把引用变量声明为一个接口类型:
ITeacher t; //这样可以
上面一行可以通过,下面一行也可以:
ITeacher t = new Professor();
为什么?如果等号右边的表达式的类型与左边变量的类型匹配,编译器就允许赋值操作。Professor类实现了ITeacher 接口,教授就是老师,所以这种赋值是被允许的。
接口的重要性
只要有可能,就应该把类的公共部分设计为接口,而不是设计为特定的类类型,这样就可以让方法在多个方面拥有更多灵活性,诸如:
方法的形参
方法的返回值
下面用两个例子来展示接口的威力。
例一
在本例中,假定
Professor是Person的一个派生类;
Student是Person的一个派生类;
Professor 和Student是兄弟类——相互不继承;
Person实现ITeacher接口,所以Professor和Student也间接地实现了ITeacher接口。
我们从设计一个名为Course的类开始,该类有一个类型为Professor的私有字段,名为teachingAssistant,和访问这个字段的属性。
public class Course
{
private Professor TeachingAssistant
}