抽象类,接口的比较

来源:互联网 发布:人工智能的原理及作用 编辑:程序博客网 时间:2024/05/17 05:19

抽象类和接口的区别


  (1)抽象类里面可以有非抽象的方法,接口里只能有抽象方法。

  (2)抽象类中的抽象方法声明时不能有大括号,而接口中的所有方法都没有大括号。
 

 抽象类(abstract class):


  a.抽象类是仅供派生的类,无法实例化一个抽象类,只能实例化从它派生的类。

  b.抽象类的主要特征在于它包含抽象成员(abstract member),抽象成员是不具有实现的一个方法或属性,其作用是强制所有派生类提供实现。

  c.由于抽象成员应当是要被重写(override)的,因此这类成员会自动成为virtual成员,而且不能这样显示地声明。

  d.抽象成员不能是private的,否则派生类看不见他们。

 

 接口(interface):


  a.接口是抽象类的变体。

  b.接口的一个关键特征是它既不包含实现,也不包含数据。

  c.字段(也就是数据)不能出现在一个接口中。

  d.接口的宗旨是定义由多个类共同遵守的一个契约(这一点很重要,如果这个契约经常变化的话,就不适用接口,这样的话接口定义会经常变化,而实现类很多也需要经常跟着变化了,所以接口中所有成员都必须为public类型。(但是对于这一点,接口需要变动的话,java可以通过如下的抽象类实现部分接口方法来灵活处理,而C#由于必须实现接口方法就不好整了,只有修改具体的实现类)


关于抽象类不用实现接口的全部方法(该结论是对于java,对于c#抽象类还是必须实现接口的所有方法)

对于java:

当你自己写的类想用接口中个别方法的时候(注意不是所有的方法),那么你就可以用一个抽象类先实现这个接口(接口方法不用全部实现),然后再用你的子类继承这个抽象类(子类可以实现父类中没有实现的方法,同时也可以重写父类中实现的方法,如果不用子类重写,则在方法前加上final关键字),这样就可以达到你的目的了,如果你直接用类实现接口,那是所有方法都必须实现的。

(这样有个好处当你的接口增加方法后,该抽象父类不用做具体的修改,尤其具体的子类去实现这些新接口方法,父类的引用指像子类的对象后一样可以调用到该方法)

public static void main(String[] args) {// TODO Auto-generated method stubRXB r = new RXBChild();r.aRxb();r.rxb();r.rxb2();}public interface IRXB{void rxb();}public static abstract class RXB implements IRXB{public abstract void aRxb();public void rxb2(){System.out.println("我是爸爸的常规方法");}//因为该类是抽象类,所以不用实现接口的全部方法//public void rxb(){//System.out.println("我是爸爸实现的接口的方法");//}}public static class RXBChild extends RXB{@Overridepublic void rxb() {// TODO Auto-generated method stubSystem.out.println("我是儿子,我实现了爸爸实现接口中的方法");}@Overridepublic void aRxb() {// TODO Auto-generated method stubSystem.out.println("我是儿子,我实现了爸爸的抽象方法");}public void rxb2(){System.out.println("我是儿子,我重写了爸爸的常规方法");}}

对于C#:

用一个类去实现这个接口(接口方法必须都实现,否则编译就不会通过),但是可以在这些实现的方法上加上Virtual关键了,那么子类继承这个类后可以重写(override)这些方法。当再用父类的引用指像子类的对象时,就可以调用子类的逻辑实现了。

    public class Program    {        public static void Main(string[] args)        {            RXB r = new RXBChild();            r.vrxb();            r.rxb2();            r.rxb();            Console.ReadLine();        }    }    public interface IRXB     {        void rxb();    }    public class RXB : IRXB    {        public virtual void vrxb()        {            Console.WriteLine("我是爸爸的虚方法");        }        public void rxb2()        {            Console.WriteLine("我是爸爸的常规方法");        }        public virtual void rxb()        {        }    }    public class RXBChild : RXB    {        public override void vrxb()        {            Console.WriteLine("我是儿子,覆盖了爸爸的虚方法");        }        public new void rxb2()        {            Console.WriteLine("我是儿子,覆盖了爸爸的常规方法");        }               public override void rxb()        {            Console.WriteLine("我是儿子,实现了接口的方法");        }    }

最后关于接口和抽象类的具体什么时候用的理解

考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示:
  使用abstract class方式定义Door:
abstract class Door{    abstract void open();    abstract void close();}

  使用interface方式定义Door:
interface Door{    void open();    void close();}


  其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。
  如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中, 主要是为了展示 abstract class 和interface 反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略)?下面将罗列出可能的解 决方案,并从设计理念层面对这些不同的方案进行分析。
  解决方案一:
  简单的在Door的定义中增加一个alarm方法,如下:
abstract class Door{    abstract void open();    abstract void close();    abstract void alarm();}

  或者
interface Door{    void open();    void close();    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 Principle),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方 法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变,反 之依然。
  解决方案二:
  既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定 义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用 abstract class 方式定义;两个概念都使用interface方式定义;一个概念 使用 abstract class 方式定义,另一个概念使用interface方式定义。
  显然,由于c#语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。
  如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有 理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分 析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用 interface方式定义)反映不出上述含义。
  如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报 警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在c#语言中表示一种继承关系,而继承关系 在本质上是"is-a"关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说 明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义。如下所示:
abstract class Door{     abstract void open();     abstract void close();}

interface Alarm{    void alarm();}class Alarm Door extends Door implements Alarm{    void open(){…}    void close(){…}    void alarm(){…}}


  这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其 实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。


0 0