java报错: 类重复: newemission.calculate_从Java中ArrayList继承的AbstractList类以及实现的List接口我们看到了什么?...

a5b65ae2ce341f19f593ed3ce77d80c0.png

其实这个话题,

本意没想要单独作为一篇文章来叙述,

咱们在上一篇跟踪java集合源码的时候,

其实是给大家留了个口,

原本是想让同学们提出来,

然后有问有答,

想着怎么也比我一人傻呵呵说效果好,

但这两日我实在等不及啦,

想来也没多少可说的,

索性我就主动点儿,

今天把这事儿给办了...


从何说起

到底咱们上文是留了个什么口呢?

大家是否还有印象,

咱们再说集合的【源码跟踪】的环节时,

ArrayList类举的例子,

(如下图示):

50a23170217146b1512884268865e613.png

来看上图红色框框圈起来的位置,

它继承了抽象类AbstractList类 ,

同时实现了List接口,

这无可厚非,

我们继续深入一层,

来看看AbstractList类,

(如下图示):

a67bb82fef4392577aae0ec94f8ccad2.png

我们发现AbstractList类在继承了AbstractCollection抽象类,

也同样实现了List接口,

子类为什么会重复继承(实现)父级已继承(实现)的接口(抽象类)呢

这不是多余了吗?

我们都知道继承关系有传递性

传递性是啥意思呢?

为了加深理解,

我们先来写一个简单例子,

传递性示例

咱们先创建一个【爷爷】类,

设置一个属性并编写一个方法,

方法很简单,

就是夹带着属性在控制台打印一句话,

(如下图示):

6bbca2d01e66267f6a6d9f8f857e37ae.png

然后创建一个【父】类,

只让它继承【爷爷】类,

类里面啥事儿也不做,

(如下图示):

94cea9928299d1d04c758b0b7912bc36.png

最后我们再创建一个【儿子】类,

此类里也只继承【父】类,

同样什么也不写,

(如下图示):

2099e9b64c58d7068a953a36992aadcd.png

接下来验证下继承关系的传递性,

我们创建一个main函数,

然后在此函数体内,

实例化【儿子】(son类)并赋值,

(如下图示):

bc84cd93a7a877d7b12aefdb63ef45d4.png

从上图我们可以看到,

实例化后的子类可以调用属性和方法,

并且能正确执行并打印出我们想要值,

这就证明继承关系是有传递性的。


举例说明

问题又回到开始,

“ArrayList类的父级AbstractList抽象类已经实现过了List接口,

为什么ArrayList还要再次实现List接口”?

为什么java源码里会重复继承(实现)呢?

要说明这个问题,

首先我们要搞明白

抽象类接口的用法,

我们都知道接口的意义在于,

定义了一种规范,一种标准

不做具体实现,

(当然JDK 1.8及以后的版本,接口有了默认和静态方法,这就另说啦);

所有继承接口的子类都要一 一实现它的所有方法,

这可能就是为什么继承类用关键字“extends”,
继承接口要用“implement”关键字吧?

既然要实现接口的所有方法就存在一个问题,

不是所有要继承接口的子类都能用到接口里的全部方法

对于继承接口那些用不到的方法也需要实现了它,

这不就造成了代码的重复嘛!

那么怎么解决这个问题呢?

我们可以只让一个类去实现这个接口,

然后其他的类继承这个类就好了嘛。

可能有同学联想到模板,

对,这就是抽象类设计时其中的一种定义,

抽象类就是一种模板式的设计

这么说可能太苍白,

理解起来不够直观,

让我们来再写个简单的例子,

示例辅助说明

我们首先创建一个接口,

(如下图示):

61f71ae383309c59b7ccf9ac511a9ecc.png

再接下来我们在创建一个抽象类,

(如下图示):

4cec6364fac14fa0004a6f7ccecfa3dd.png

我们在测试类里继承一下,

分别看看效果,

先来继承(implements)下接口,

(如下图示):

cab6ad066b72cb6648af9ecc8993f4b5.png

发现继承接口后程序报错了,

我们将鼠标放到红色波浪线看下错误提示,

需要我们将接口内的方法给实现喽

那我们根据提示将方法salad()实现一下,

(如下图示):

93d97cb7debb053ab4e00477e1be93bf.png

我们发现报错并未解除,

我们点开提示发现,

(如下图示):

bdf2365f8613ba95ca8e4649208e8399.png

根据上图我们看到继承了接口后,

如果不实现接口里方法就会报错,

并提示我们有4个方法待实现,

实现全部方法后的样子,

(如下图示):

71cc2bb6dc1c4f8f61bf812a07403aed.png

我们发现这个时候异常算是解除啦…

那么抽象接口呢?

(如下图示):

2a9da83264ed30ef033450282e451fa1.png

我们它没有报任何错误…

并且我们可以根据我们的需求,

实现我们想要实现的方法即可,

比如我们实现salad()方法,

(如下图示):

50bb90184c12c904e37c9d8314150d77.png

没有任何的问题….

假如我们有个需求,

除了使用TomatoAbstract里的cooking()方法,

还需要使用FruitInterface里的eat()方法,

按照以往的方式,

我们会这么用,

(如下图示):

c25c66a8897975f0d746ef162c7f6195.png

可我们只需要用到(标红)两个方法,

就不得不实现那些我们用不到的方法...


解决问题

我们根据上文的分析可以将代码调整下,

或许能解现在的尴尬问题,

我们先进入到TomatoAbstract抽象类,

做如下改动,

(如下图示):

a0525d4ca9e7bbbefab7d0f3888fefe4.png

将我们不需要使用的方法,

size()、name()等先在抽象类中实现

然后切换到我们需要使用的抽象类的方法中,

继承这个TomatoAbstract模板(抽象)类,

(如下图示):

55fcf141372c16c517cfdf595cd2cddf.png

我们发现目前必须要实现的方法只有一个eat(),

实现后我们再来看,

(如下图示):

41c6c1c4f00417cc104c9c622a22c0ae.png

报错的现象已经解除,

并且此方法也是我们需要使用的方法,

这时候你会说我们用到的还有一个cooking()方法呢?

这个时候我们手动将它实现即可,

(如下图示):

48ab30e6f33e6f6fc522b18d305fa8c9.png

改造后代码是不是简洁、优雅多啦,

这样不仅避免了重复代码、耦合性也更低啦;


为什么会重复继承

有同学可能会说,

这也没有说明白JDK源码里为什么要重复继承的问题啊?

其实JDK源码里之所以重复引用

更类似于一种编码规范

为什么这么说呢?

以我们的demo为例,

(如下图示):

b8ef8915a19afc7694c64578bf229074.png

从上图我们只能看到此类继承了抽象类TomatoAbstract,

不能直观的看出eat()方法实现自FruitInterface接口,

所以为了规范我们代码应该改成,

(如下图示):

d2fd80a5f065d3a0ce76bbcd50277949.png

这样我们就能很容易判断...

以上这两个函数分别继承自FruitInterface 接口和 TomatoAbstract抽象类。

OK啦!!!!

你:就这?

我:就这...

hahahahahaha......

下一篇见…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值