接口与抽象很多方面相似,下面列出其共同点。
1.都不能创建实例对象,因为他们都是抽象的。
2.虽然不能直接通过关键字“new”创建实例对象,但可以声明变量,通过变量指向子类或实现类的对象,来创建实例对象。
两者也有不同点,如下所示。
1.Java不支持多重继承,即一个子类只能拥有一个父类,抽象类也是如此。但一个子类可以实现多个接口。
2.接口内不能有实例字段,只能有静态常量。抽象类可以有实例字段。
3.接口内的方法自动设置为“public”的。抽象类中的方法必须手动声明访问控制符。
抽象类和接口在概念上的区别
声明方法而不去实现它的类被称作抽象类,它用于创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现这些方法的情况。不能创建abstract类的实例,但可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例,不能有抽象构造函数或抽象静态方法。abstract类的子类为其父类中的所有抽象方法提供实现,否则也是抽象类。
接口是抽象类的变体,在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口只可定义“static final”成员变量,接口的实现与子类相似,但该实现类不能再接口定义中继承行为。
当类实现特殊类接口时,需要实现这种接口的所有方法,然后,它可以在实现该接口的类的任何对象上调用接口的方法。抽象类允许使用接口作为引用变量的类型,引用可以转换到接口类型或从接口类型转换,“instanceof”运算符可决定某对象的类是否实现了接口。
从设计理念上看待抽象类和接口
“abstract class”在Java语言中体现了一种继承关系。要想继承关系合理,父类和派生类之间必须存在“is a”关系,即父类和派生类在感念本质上应该是相同的。对于“interface”来说则不然,其并不要求“interface”实现者和“interface”定义在概念本质上是一致的,仅仅是实现了“interface”定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。
考虑这样一个例子。假设有一个关于“door”的抽象概念,该“door”执行两个动作:“open”和“close”。此时可通过“abstract class”或者interface来定义一个表示该抽象概念的类型。定义方式分别如下所示:
使用抽象类来定义door:
abstract class door{
abstract void open();
abstract void close();
}
使用接口来定义door:
interface door{
abstract void open();
abstract void close();
}
其他具体的“door”类型可以继承“abstract class”或者“interface”定义的“door”。看起来使用两者没什么大的区别,如果现在要求“door”还具有报警功能呢,该如何设计针对该例子的类结构呢?本例主要是为了展示“abstract class”和“interface”反映在设计理念上的区别,并从设计理念层次面对这些不同的方案进行分析。
【解决方案一】
简单的在“door”的定义增加一个“alarm”方法,如下所示:
abstract class door{
abstract void open();
abstract void close();
abstract void alarm();
}
或者:
interface door{
abstract void open();
abstract void close();
abstract void alarm();
}
那么具有报警功能的“alarmdoor”的定义方式如下:
class alarmdoor extends door{
void open(){..}
void close(){..}
void alarm(){..}
}
或者
class alarmdoor implements door{
void open(){..}
void close(){..}
void alarm(){..}
}
这种方法违反了面向对象设计的一个核心原则ISP(interface segregation priciple)。在“door”的定义中,把“door”概念本身固有的行为方法和另一个概念“报警器”的行为方法混合在了一起。如果是这样,那些仅仅依赖于“door”概念的模块,会因为“报警器”这个概念的改变而改变,反之亦然。
【解决方案二】
既然open、close和alarm属于两个不同的概念,根据ISP原则应该把他们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用“abstract class”方式定义;两个概念都使用“interface”方式定义;一个概念使用“abstract class”方式定义,另一个概念使用“interface”方式定义。
显然,由于java不支持多重继承,所以方式一不可行。后面两个都可行,但是对他们的选择,应该反映出对于问题领域中的概念本质的理解,对于设计意图的反映是否正确、合理。下面一一来分析。
如果两个概念都使用“interface”方式来定义,就反映出下面问题:
□可能没有理解清楚问题领域,“alarmdoor”在概念本质上到底是“door”还是报警。
□如果对于问题领域的理解没有问题,比如:通过对于问题领域的分析发现“alarmdoor”在概念本质上和“door”是一致的。那么,在实现时就没有能正确的揭示设计意图,因为在这两个概念的定义上(均使用interface定义)反映不出上述含义。
□如果对于问题领域的理解是“alarmdoor”在概念本质上是“door”,同时它具有报警的功能。该如何明确的反映出上述意思呢?“abstract class”在java语言中表示一种继承关系,所以对于door这个概念,应该使用“abstract class”方式来定义。另外,“alarmdoor”又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过“interface”方式定义
class door{
abstract void open();
abstract void close();
}
interface Alarm{
void alarm();
}
class alarmdoor extends door implements Alarm{
void open(){..}
void close(){..}
void alarm(){..}
}
如果认为“alarmdoor”在概念本质上是报警器,同时又具有“door”的功能,那么上述的定义方式就要反过来。