• java--------抽象类与接口的区别


    1、抽象类与接口的抽象层次是不同的 
    抽象类是对类抽象,接口是对行为抽象。类包含了属性与行为,所以说接口是更具体的抽象。

    2、抽象类与接口的设计层次是不同的 
    抽象类是一种自下而上的设计,先有子类才能提取公同的属性与行为,抽象出父类,意思讲:子类的共同属性抽象出来的类就是父类,抽象类主要是规定/定义一个族群的祖先; 
    接口是一种自上而下的设计,先规定行为方法,只要可以实现这些行为,就可以成为接口的实现类,因为inteface具有多继承性。

    3、抽象类与其派生类的关系和接口与其实现类的关系本质是不同的 
    抽象类与其派生类是一种“is-a”关系,即父类和派生子类在概念上的本质是相同的(父子关系,血缘联系,很亲密)。 
    接口与其实现类是一种“like-a”关系,即接口与实现类的关系只是实现了定义的行为,并无本质上的联系(契约关系,很淡漠的利益关系)。

    举个例子:比如说一个动物抽象类,定义了跑的方法、叫的方法,但如果一个汽车类可以实现跑、可以实现叫,它就可以继承动物抽象类吗?!这太不合理了,汽车不是动物呀!而如果通过接口定义跑的方法、叫的方法,汽车类作为实现类实现跑和叫,完全OK很合理,就因为没有继承关系的约束。

    为了更好的阐述他们之间的区别,下面将使用一个很棒的例子来说明。该例子引自博文链接

    我们有一个Door的抽象概念,它具备两个行为open()和close(),此时我们可以定义通过抽象类和接口来定义这个抽象概念:

    抽象类:

    abstract class Door{  
        abstract void open();  
        abstract void close();  
    }  
    • 1
    • 2
    • 3
    • 4
    • 1
    • 2
    • 3
    • 4

    接口:

    interface Door{  
        void open();  
        void close();  
    }  
    • 1
    • 2
    • 3
    • 4
    • 1
    • 2
    • 3
    • 4

    至于其他的派生类可以通过: 
    1、使用extends使用抽象类方式定义Door 
    2、使用implements接口方式定义Door

    这里发现两者并没有什么很大的差异,但是现在如果我们需要门具有报警的功能,那么该如何实现呢?

    解决方案一:给Door增加一个报警方法:alarm();

    abstract class Door{  
        abstract void open();  
        abstract void close();  
        abstract void alarm();  
    }  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

    或者

    interface Door{  
        void open();  
        void close();  
        void alarm();  
    }  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

    这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle)—见批注,在Door的定义中把Door概念本身固有的行为方法和另外一个概念”报警器”的行为方法混在了一起(意思就是说:Door(抽象类)的属性是所有门的共同特性,而具有报警器属性的门只有报警门所具有,如果把报警特性放到Door中,假设要定义一个不报警的门就需要修改Door,但是报警门又extends Door,所以这一改就会牵动很多其他的类,因此面向对象设计中有一个核心原则ISP)。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为”报警器”这个概念的改变而改变,反之依然。

    解决方案二: 
    既然open()、close()和alarm()属于两个不同的概念,那么我们依据ISP原则将它们分开定义在两个代表两个不同概念的抽象类里面,定义的方式有三种: 
    1、两个都使用抽象类来定义。 
    2、两个都使用接口来定义。 
    3、一个使用抽象类定义,一个是用接口定义。

    由于java不支持多继承所以第一种是不可行的。后面两种都是可行的,但是选择何种就反映了你对问题域本质的理解。

    如果选择第二种都是接口来定义,那么就反映了两个问题: 
    1、我们可能没有理解清楚问题域,AlarmDoor在概念本质上到底是门还报警器。 
    2、如果我们对问题域的理解没有问题,比如我们在分析时确定了AlarmDoor在本质上概念是一致的,那么我们在设计时就没有正确的反映出我们的设计意图。因为你使用了两个接口来进行定义,他们概念的定义并不能够反映上述含义。

    第三种,如果我们对问题域的理解是这样的:AlarmDoor本质上Door,但同时它也拥有报警的行为功能,这个时候我们使用第三种方案恰好可以阐述我们的设计意图。AlarmDoor本质是们,所以对于这个概念我们使用抽象类来定义,同时AlarmDoor具备报警功能,说明它能够完成报警概念中定义的行为功能,所以alarm可以使用接口来进行定义。如下:

    abstract class Door{  
        abstract void open();  
        abstract void close();  
    }  
    
    interface Alarm{  
        void alarm();  
    }  
    
    class AlarmDoor extends Door implements Alarm{  
        void open(){}  
        void close(){}  
        void alarm(){}  
    }  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    批注: ISP(Interface Segregation Principle):面向对象的一个核心原则。它表明使用多个专门的接口比使用单一的总接口要好。 
    一个类对另外一个类的依赖性应当是建立在最小的接口上的。 
    一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染

  • 相关阅读:
    大数据量下协同推荐的难点与优化方法
    几个常见的Mysql索引问题
    Orchard CMS -Migration文件更新后数据库不更新的问题 new properties not updating after migrationData migration is not working?
    一个可以设置中奖概率的抽奖程序[转]
    Implementing HTTPS Everywhere in ASP.Net MVC application.
    通过jQuery Ajax使用FormData对象上传文件
    SqlServer 数据去重
    向json中添加新的熟悉或对象 Add new attribute (element) to JSON object using JavaScript
    Dapper的基本使用,Insert、Update、Select、Delete
    DapperExtensions and Dapper.Contrib在表构架不是默认dbo时的处理 DapperExtensions and Dapper.Contrib with non-dbo Schema
  • 原文地址:https://www.cnblogs.com/w-wfy/p/6409755.html
Copyright © 2020-2023  润新知