专栏首页me的随笔抽象类 VS 接口

抽象类 VS 接口

引言

接口和抽象类是面向对象编程(OOP, Object Oriented programming)中两个绕不开的概念,二者相似而又有所不同。接下来,我们来了解二者的概念并比较它们的异同。

什么是抽象类型?

抽象类是一种特殊的类,该类不能被实例化。抽象类的存在就是为了被继承,即抽象类可以被其它类继承但不能被实例化。那么,我们为什么需要一个无法被实例化的类呢?这样做的优点是,通过抽象类我们制定了一份强制所有子类必须遵守的合约,使所有子类有着一致的层次结构。抽象类提供了一种规范用于规定子类如何进行工作,子类可根据自身情况来重写抽象类中的抽象成员(及其它可被重写的成员)以满足自身需求。

抽象类作为一个基类,可以包含已实现的成员,同时应至少包含一个抽象成员,否则就没必要使用抽象类了。如果一个抽象类中仅仅包含抽象方法,那么这时抽象类就和接口很像了。

什么是接口?

接口中不能包含任何被实现的成员,即接口中只能包含成员的签名。如,没有方法体的方法、只包含访问器关键字(set、get)的属性等。和抽象类类似,接口也是一份合约。C#中,接口和抽象类的主要区别是,类可以实现多个接口,但只能继承一个(抽象)类。

比较异同

特征

接口

抽象类

是否支持多继承

支持

不支持

默认实现

接口中不能包含任何已实现的成员

抽象类中可以包含已实现(非抽象)的成员

访问修饰符

接口成员默认是公共(public)的,不再允许被任何访问修饰符修饰

抽象类成员可以被访问修饰符(不能是private)修饰

核心 VS 辅助

接口多用于定义(辅助性的)能力

抽象类多用于定义相同类型(这里类型不是数据类型的意思,解释见下文)子类所共有的一些特征

若只提供一些方法上的约束,建议使用接口

如果子类属于同一类型,且具有相同的行为或状态,建议使用抽象类提供约束

寻找成员速度

相比抽象类较慢

相比接口更快

成员变动的影响

如果接口成员发生改动,则所有实现类都要进行改动

若向抽象类中添加非抽象成员,我们可以给该成员提供默认实现,这样子类就无需发生变动

允许包含的抽象成员

属性、方法、事件、索引器(这四类本质上都是方法)

属性、方法、事件、索引器

是否允许定义字段

不允许

不允许定义使用abstract修饰的字段

抽象类是对子类的抽象,即将子类中的公共部分提取出来,放到一个特定的类中。抽象类是一份合约,用于为同一类型(这里类型不是指数据类型,而是逻辑上的划分,如人和猫都是动物)的子类提供约束。 接口也是一份合约,但接口多用对能力的定义,即用于指定实现类能做哪些事儿。

人和猫,都属于动物这个大类,我们可以抽象出二者的公共部分。如,年龄、体重、吃、会叫(用于形容人不太友好)等作为一个抽象类的成员。

abstract class Animal
{
    public abstract int Age { set; get; }
    public abstract float Weight { set; get; }
    public abstract void Call();
    //默认实现
    public virtual void Eat()
    {
        Console.WriteLine("猫粮......");
    }
}


class Person : Animal
{
    public override int Age { set; get; }
    public override float Weight { set; get; }

    public override void Eat()
    {
        Console.WriteLine("鱼香肉丝盖饭......");
    }

    public override void Call()
    {
        Console.WriteLine("雷好啊......");
    }
}

class Cat : Animal
{
    public override int Age { set; get; }
    public override float Weight { set; get; }

    public override void Call()
    {
        Console.WriteLine("喵喵喵......");
    }
}

那么,只要继承Animal类的类都应属于动物这个大类,汽车就不应该继承Animal类。 此外,人和猫相比,人会制作工具而猫不行,那么制作工具的技能就是人所特有的,这时可以定义一个接口提供对制作工具这项技能的约束,然后让Person类实现该接口。

interface ICreatTool
{
    void CreateTool();
}

class Person : Animal, ICreatTool
{
    public override int Age { set; get; }
    public override float Weight { set; get; }

    public override void Eat()
    {
        Console.WriteLine("鱼香肉丝盖饭......");
    }

    public override void Call()
    {
        Console.WriteLine("雷好啊......");
    }

    public void CreateTool()
    {
        Console.WriteLine("造台MAC......");
    }
}

再如,人、车、猫三者都可以跑,那么也可以定义个接口用于对跑这项技能提供约束。

interface IRun
{
    void Run();
}

class Person : IRun
{
    public void Run()
    {
        Console.WriteLine("11路公交......");
    }
}

class Cat : IRun
{
    public void Run()
    {
        Console.WriteLine("四蹄奔腾......");
    }
}

class Car : IRun
{
    public void Run()
    {
        Console.WriteLine("四轮......");
    }
}

小结

用简单的话概括接口和抽象类的异同:

  • 抽象类和接口都是一种约束,这种约束使我们的代码有更好的层次结构,特别是在多人协同开发时(若每个人都按照自己的习惯来,对整个开发团队而言,开发成本不知要提高多少)。
  • 抽象类是对相同类型(不是数据类型)子类公共部分的抽象(约束),接口是对能力的一种约束。

此外,建议大家读一读文末给出的这篇文章,本人读完收益颇多,本文中的表格部分引自该文。

参考文章

Abstract Class versus Interface

版权声明

本文为作者原创,版权归作者雪飞鸿所有。 转载必须保留文章的完整性,且在页面明显位置处标明原文链接

如有问题, 请发送邮件和作者联系。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 模板方法模式实践

    在实际编程中,会经常遇到多个类中的某些方法实现逻辑类似的情况,这时我们可以将这些类中的相同部分抽象到父类中,对于有差异的地方,子类根据自身的实际需求来各自实现。

    雪飞鸿
  • 使用CodeFirst创建并更新数据库

    在使用Entity Framwork的三种方式(ModelFist、DBFirst、CodeFirst)中,CodeFirst方式书写的代码最为干净。

    雪飞鸿
  • 使用Visual Studio 2015 Community 开发windows服务

      昨天研究在.NET下开发Windows服务程序,期间遇到一些小问题,这里仅将自己的开发过程和需要注意的地方写下和广大网友分享……

    雪飞鸿
  • 使用抽象类和接口的优解

    1. 前言 笔者相信,每个使用面向对象语言的开发者自编码以来,肯定便琢磨过抽象类(Abstract)和接口(Interface)的区别。可能一些人已经找到了适...

    潘成涛
  • 使用抽象类和接口的优解

    潘成涛
  • 接口和多态性

    如果你又加了一个百度外卖,那么eat函数中又要new 一个BaiDu() ,给开发带来麻烦。我们希望的是,如果代码要扩展了,那么代码要尽最大可能的进行很小的改动...

    ShenduCC
  • Java 学习笔记(11)——lambda 表达式

    在写Java代码的时候,如果某个地方需要一个接口的实现类,一般的做法是新定义一个实现类,并重写接口中的方法,在需要使用的时候new一个实现类对象使用,为了一个简...

    Masimaro
  • ASP.NET Core中间件与HttpModule有何不同

    在ASP.NET Core中最大的更改之一是对Http请求管道的更改,在ASP.NET中我们了解HttpHandler和HttpModule但是到现在这些已经被...

    HueiFeng
  • Head first 设计模式之策略模式,来源于生活,用之于生活

    设计模式是软件设计中常见问题的通用可重用的解决方案,与语言无关。通过引入设计模式,可以更好的提高代码复用性、灵活性、扩展性。

    用户4361942
  • 【译】谈谈2019年的RUST和WebAssembly

    将 Rust 编译到 WebAssembly 对 Web 来说是快速、可靠的最佳选择。另外,Rust 与本地 C 语言调用约定和库集成的方式相似,Rust 还应...

    腾讯IVWEB团队

扫码关注云+社区

领取腾讯云代金券