在设计空调车类时,可以有如下两种选择:
第一,继承(Extends)现有的“汽车(Bus)”类,完善定义在Bus类的一些方法,并增加“提供空调服务”方法,来实现“空调车(AirConditionedBus)类。
第二,通过实现(Implements)现有的提供空调功能的接口,为空调车类引入空调的服务,并在此基础上定义空调车的其它动作。
这两种做法都对,不过我们需要根据项目的实际需求来选用具体的方案。
如果从公交公司的角度考虑,他们希望空调车的第一要素是“车”,在此基础上提供“空调的服务”,所以,会倾向继承“汽车”实现“空调车”的方案。因为他们更可以通过继承在“Bus”类里的一些共性,实现一些诸如Taxi等的类。
如果从空调生产厂商的角度考虑,他们更关心实现空调功能的方式。所以他们会把实现空调功能的共性代码以接口的形式归纳,这样,他们就可以用实现空调接口的方式,在船和飞机上,安装上空调的功能。
最后你可以说,我我们项目设计的诸多场景里,会根据实际需求设计抽象类和接口,抽象类是对概念的归纳,接口是对功能的归纳。
如下是从语法角度对接口和抽象类的进一步归纳。如果父类是个抽象的概念而非实体,我们可以把这个类定义成为抽象类,比如我们可以把动物类定义成抽象类,代码如下。
abstract class Animal {
abstract void run();
}
在第1行里,我们在类之前加了abstract,由于动物是个抽象的概念,第2行定义的run方法是抽象想法,由于没有实际的意义,所以没方法体。
抽象类是对概念的抽象,比如我们可以定义人类这个抽象类,它的子类可以是中国人或美国人等,而接口是对功能的抽象,比如我们可以定义提供“发电”功能的接口,它的实现类可以是火力发电机类或水力发电机类。
interface GeneElec{
int power = 220;
void generate();
}
在定义的GeneElec这个接口里,第3行的generate方法没有方法体,如果加了反而会报错。 这个原因和抽象类的非常相似,因为接口是对功能的抽象,对于抽象的动作定义方法体没有意义,所以,就从语法上杜绝了这种没有意义的动作。
在类里,如果没有特殊需求,出于封装的考虑,属性需要定义成privat。但在接口里,由于不能定义方法体,所以无法定义属性的get和set方法,所以只能把属性的默认修饰符定义成public的,而且由于接口是对功能的抽象,不包含具体的业务实现,所以只能定义具有通用性质的常量,比如在第2行里定义通用电压是220伏, 而包含业务的变量是放到接口的实现类里定义的。
所以如果我们不给接口的属性加修饰符,比如像第2行那样,那么默认的修饰符是public static final,其中用public来表示公有,static final表示这是个常量。