抽象类,接口的比较

1 篇文章 0 订阅

抽象类和接口的区别


  (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 stub
		RXB 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{

		@Override
		public void rxb() {
			// TODO Auto-generated method stub
			System.out.println("我是儿子,我实现了爸爸实现接口中的方法");
			
		}

		@Override
		public void aRxb() {
			// TODO Auto-generated method stub
			System.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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值