JAVA接口和抽象类的区别?为什么有了接口还要抽象类?为什么接口越来越像抽象类?

1.什么是接口什么又是抽象类

对于初学者来说往往分不清什么是接口什么又是抽象类,这两个到底是做什么的有什么区别?对于一些有开发经验的来说除了语法之外也很难说清它们的具体的区别,只知道接口中只能声明方法,抽象类可以定义抽象方法由子类进行实现。
而网络上大部分文章对接口和抽象类给出的定义是这样的:
对于接口有如下定义:
1.Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)
2.在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法
对抽象类有如下定义:
1.由abstract修饰的方法叫抽象方法,由abstract修饰的类叫抽象类。
2.带有抽象方法的类称之为抽象类

以上两段对接口和抽象类的定义是笔者在网上百度的,非常官方,非常笼统,非常难以理解。
不光初学者不理解什么意思,有一些开发经验的也很难说清这两个具体什么意思或者对这两个的意义含糊不清。
那笔者就对接口和抽象类进行一些阐述,首先接口和抽象类都有一个共有的概念那就是 “抽象”。何为抽象?在生活中老是能听到用来描述画说这是一幅抽象画(其实现实中对画作进行评价说这幅画很抽象,这个抽象往往是指读不懂的意思,这幅画很抽象说白了就是我看不懂你到底画个了啥?),但抽象这个词用在变成语言里就变得难以理解。
那何为抽象?笔者给出的定义是 “是对现实事物或虚拟事物的客观描述,也就是抽取事物的共性或特征而摒弃事物的具体细节”
如何来理解这一句话,打个比方比如 “我需要一台车” 就这句话就很抽象,因为我没说我需要什么车,是汽车还是自行车还是摩托车还是火车,或者说符合带能带人出行的陆地交通工具都能称之为车。这就是抽象。有抽象就有具体什么是具体?具体是对于抽象事物的直接描述。
相对于 “我需要一台车” 这句抽象需求来说,我直接给一辆汽车或自行车或者只要符合车这个概念的物体是不是就满足了这个需求?这是不是就是对抽象事物的直接实现。那如果把上面的这个需求写为代码呢

public interface Vehicle {
	String getVehicle(); 
}

public class Car implements Vehicle {
	public String getVehicle(){
		return "我是汽车";
	}
}

public class Bicycle implements Vehicle {
	public String getVehicle(){
		return "我是自行车";
	}
}


public static void main(String[] args) {
    Vehicle v = new Car();
}

通过以上代码是不是对抽象有了更客观地认识,Vehicle 接口的getVehicle();方法是对 “我需要一台车” 这句话的定义或者说抽象。那么CardBicycle类中的getVehicle();则是对这句话的具体实现,笔者只是拿自行车和汽车当做了实现,但对于getVehicle();的扩展其实是无限的,读者完全可以再把摩托车、火车、电瓶车等等加上。
这就是抽象到具体的实现过程,笔者是以 “我需要一台车” 为例的,这句话其实是描述的一个需求。
那如果我们通过接口来定义或描述一个物体呢?例如定义一台汽车,那这样笔者就下一个定义,只要有四个轮胎、有驱动引擎、有方向盘的就算作汽车。
那么只要实现这个接口的类是不是就可以算作汽车类,只要通过new指令实例化出来是不是就造了一辆汽车。
那么对于这几个方法的拓展理论上也是无限的,例如轮子可以是四个橡胶轮子也可以是木轮子也可以是热熔胎等等等,驱动引擎可以是汽油的也可以是柴油的还可以是电的。此处就不一一举例。

public interface Car {
	
	//四轮
	String fourWheels(); 
	
	//驱动引擎
	String engine();

	//方向盘
	String steeringWheel();
	
	//车身颜色
	String color();
}

那根据如上规则是不是只要实现了Car接口的实现类,并且使用new进行了实例化就可以代表造了一辆汽车。
详细读者看到这里就明白了抽象的含义了,希望读者可以再加深对于抽象的理解,因为抽象这个概念相当重要,任何一个语言都有抽象的概念在里面不光JAVA中存在,C++、Golang等语言都有抽象的概念在里面。并且抽象是实现 开闭原则 的基石,而23种设计模式又是对开闭原则的实现,如果没有抽象就没有设计原则,如果读者能看懂以上的例子那么读 Java设计模式 这本书或者说了解23种设计模式将会非常轻松。
通过上面的例子还有一些问题还需要阐述明白
1.接口和抽象的关系
接口就是对于抽象的描述,他们的关系是一对一的,接口中的每一个方法其实就是对符合抽象概念的约束,只有符合了这些约束才说明满足了抽象条件。当然接口中可以不定义任何方法,因为定义一个接口本身就已经说明了问题,其实也就是是与否的关系。例如JAVA中的SerializableCloneable这两个接口,并没有声明任何方法,但只要实现这俩接口的对象就可以进行序列化和克隆,这俩就是一个标识性接口非是即否的。
从上面这些定义是不是也从侧面印证或说明了接口即约束,那约束可不可以理解为规范。
2.抽象度问题
从第一个例子 “需要一台车” 的需求来看,这句话是不是太抽象了,也就是范围太广了,在实际开发中我们对于接口的定义是不应该太广的,也就是不要太抽象。对于太抽象或太广的定义我们应该作为基础接口然后让其他接口来基础此接口来让需求进行细化,例如在定义两个接口分别为 “需要一台汽车”“需要一台自行车” 然后让这俩继承 “需要一台车” 的接口来进行需求拆分或需求细化。
或者直接缩小需求范围但不要太细应该要得体流出一定的活动空间来,例如直接将需求定位为 “需要一台汽车” 这样的合理需求, 而不要细致到 “需要一台别克七座商务SUV、新款、无甲醛味” 这样就失去了抽象的意义,也不体现抽象而体现到了具体。
所以正常开发来说对接口的定义不要太抽象,太抽象面太大太宽泛。也不要太细 、太细就无法扩展降低系统的灵活性。其实七大设计原则的接口隔离原则就是讲的就是对抽象度进行约束,书中写的是事务定义的范围其实是第二个例子但笔者是以需求举的例子,其实当前文章的第二个例子更合理一点,但理解本质就好。

什么是接口

其实上面的例子已经阐明了什么是接口,理解了抽象也就理解了接口,此处不再赘述。

什么是抽象类

理解完接口,抽象类就更好理解了。
抽象类就是在接口的基础上增加了默认实现,在JAVA语法中抽象类的声明使用abstract关键字来修饰,只要当前类中的方法有一个方法是抽象方法(也就是只有方法声明没有方法实现)那么当前类就是一个抽象类。
此处复用第二个例子的代码,将interface改为一个抽象类。

public abstract class Car {
	
	//四轮
	String fourWheels(){
		return "轮子默认是橡胶的";
	}; 
	
	//驱动引擎
	String engine(){
	return "轮子默认是燃油的";
	};

	//方向盘
	String steeringWheel(){
		return "方向盘默认是圆的";
	};
	//车身颜色
	abstract String color();
}

此处不在对抽象类进行语法上的阐述,只从功能上进行说明用来对接口进行区分。
抽象类的定义和接口的定义是一致的,都是对具体事务或虚拟事务的描述定义,只不过抽象类增加了默认实现。
此处的默认实现是实现指这个事务的某一项特性的通常状态,例如汽车的轮子通常都是橡胶的,方向盘通常是圆的,那么我对汽车这一事务进行定义的时候是不是就可以默认轮子是橡胶的,默认方向盘是圆的。
但是对于车身颜色就不好下默认定义了,只能交给实现类进行实现了,当然实现类实现抽象方法的同时也可对父类方法进行重写来提升可扩展性。
以上就是对于抽象类的定义,抽象类的意义是非凡的,作用甚至比接口还要突出23种设计模式主要实现还是通过实现类来进行实现,其中“建造者模式”和“模板方法模式”这两种模式对抽象类还有更巧妙的运用。

2.为什么有了接口还要抽象类?

如果读者完整的读完上面的接口和抽象类的区别估计心里已经有了大概的想法,虽然上面没有直接说明这俩的区别但估计理解到位的话其实就一句话
“从语法的定义上来说就是一个有默认实现一个没有” 就是这么简单。

那么问题来了为什么有了接口还要抽象类?既生瑜何生亮?
既然这俩功能类似抽象类比接口还多了默认实现,直接用抽象类不完了还要接口做什么?
笔者是这样认为的,在JAVA中接口更相当于约束规范,而抽象类更相当于对于约束规范的简单实现或者说是通用实现也可以说是默认实现。
也就是抽象类实现接口对接口方法进行默认实现,对于多变的部分交给具体实现类去做。
接口 <— 抽象类 <---- 具体实现类
当然也不是绝对的非要按照上面这种进行设计,对于要求系统拓展性较高的应该需要按照上面这种进行扩展,对于系统扩展性较低的以下两种也是可以的
接口 <---- 具体实现类
抽象类 <---- 具体实现类

如果读者阅读过Spring源码,就会发现第一种方式被大量使用,并且Spring对抽象类的命名比较规范一般是以Abstract开头或者Default开头。
接口相较于抽象类有如下优点

  1. 接口更加简洁,开发人员只需要关注这个接口是做什么的,根据方法名称和方法的文档注释直接就能读懂这个方法是做什么用的,然后可以一目了然的看到传参和回执参数,没有任何赘述。
  2. 更加透明,看不到任何累赘的东西,就是对事务的定义。而抽象类可能就做不到这点,你不知道里面定义的这些方法是不是一定是事物的约束(被abstract修饰的方法除外)。例如目前Car对象里面只有四个方法是对于汽车的约束,如果其中一个方法调用了当前类中的子方法,那抽象类相当于有五个方法了,但那个子方法可不是构成汽车的必要特性,开发人员看抽象类的代码时可能一时间摘不清哪些方法是必须的了,也就是混入了必要特性定义之外的其他方法。这也就是接口相较于抽象类更加透明的原因
  3. 接口定义的方法必定会被实现,也就是接口的实现类中不管定义了多少方法,接口定义的方法绝对会被实现。比如一个接口中定义了一个get()方法,不管这个接口有多少实现类,实现类中定义了多少方法这个get()方法一定是会被实现的。
    以上三点是笔者认为有接口和实现类的原因。如果读者对以上三点全部理解的话接口 <— 抽象类 <---- 具体实现类这种方式的优点就一目了然了。

3.为什么接口越来越像抽象类

如果读者完全读懂了**为什么有了接口还要抽象类?**这个问题那么这第三个问题就纯属子虚乌有了,其实这个问题的本质是问为什么有了抽象类在JDK1.8版本还要对接口增加默认实现和静态方法。
那笔者就来回答一下这个问题
笔者认为JDK1.8版本对接口增加默认实现和静态方法但同时也增加了一个新特性 Lambda表达式即函数式编程,此特性主要依赖于接口完成,但对接口有要求,那就是对接口只能有一个抽象方法(除Obejct对象定义的方法之外的)并且可以增加@FunctionalInterface注解用来辅助对接口进行效验。例如SupplierPredicateFunctionConsumer这些接口都是JDK1.8版本新增的函数式接口当然还有其他的此处笔者就不一一列举,但这些接口大部分都定义了default方法和static方法,有的还不止定义了一个。
那么笔者要说的是 JDK1.8版本对接口增加默认实现和静态方法其实是辅助Lambda表达式,因为一个接口只能定义一个函数式接口,功能太单一了复杂的功能完成不了,以追加默认实现和静态方法的方式进行,为什么一个接口只能定义一个函数式接口笔者认为这是一个无奈之举,JAVA已经流行了20多年,新的特性需要能运行老版本的class文件,注定有些特性只能通过语法糖的方式进行实现 (是的JAVA的Lambda表达式就是语法糖,就是一中编译器行为,底层还是以匿名内类的方式进行实现,不是从JVM虚拟机层面增加的新特性),例如JDK1.2版本编译的class文件能在JDK1.8版本吗?肯定是能的,但如果从虚拟机层面解决,那可能就很难了因为现在的java虚拟机已经很庞大臃肿了,如果函数式编程的新特性融入虚拟机,那么对于虚拟机来说可能变动就不是一点半点了,那么干脆也别那么复杂了直接让javac编译成匿名内部类不完了。还有一项就是JAVA是一门面向对象的语言,注定有一个对象的维度,但是加上对象这个维度就显得不伦不类而又难以理解,所以JAVA的函数式编程这么难用,JAVA泛型也是语法糖底层存在类型擦除。
回到接口的定义上 因为一个接口只能定义一个函数式接口,功能太单一了复杂的功能完成不了,以追加默认实现和静态方法的方式进行
如何理解?以Function接口为例,如果此接口没有增加andThen()方法那么如何实现调用完一个apply()方法再调用另一个apply()方法。方法肯定是有的只是实现起来比较麻烦,无非是调用完apply()再手动调用一下apply()呗,但不能每次遇到这个场景就手动写一下呀。
那再增加一个接口来完成这件事?对于一个新特性是不能如此草率的设计的,我们应该保留功能的完整性,对功能进行扩展而不是让这个功能成为鸡肋,所以就要在Function接口中增加方法来拓展此功能。
对于为什么通过默认方法和静态方法的方式进行拓展,底层如何实现笔者后期再发文章阐述,当然也是为了加深理解
总体来说JDK1.8版本对接口增加默认实现和静态方法其实是辅助Lambda表达式来完成功能拓展的而不是为了替代抽象类的

以上就是笔者对接口和抽象类的全部理解,由笔者手写,转载请注明出处。
有问题可在文章下方进行评论进行沟通交流加深理解

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值