首页 新闻 会员 周边

抽象方法是怎么实现子类统一的入口的?父类.对象方法;又不能访问;指向父类方法;方法又是不完整的

0
悬赏园豆:10 [已解决问题] 解决于 2017-04-23 22:07

如果我把抽象类的抽象方法注释掉了;maxArea()里的.area()会报错;
shapes[0] = new Circle(1);完成向上构造;shapes[0].area();向上构造看引用类型;如果点的父类方法;那是不完整的;如果是指向对象的方法;父类又不能访问子类;
那么shapes[0].area();.的到底是谁的?为什么?抽象方法为什么是子类统一的入口?
请你帮我解释下好吗?

public class ShapeTest {
public static void main(String[] args) {
//Shape s = new Shape(); //编译错误,抽象类不能被实例化
Shape[] shapes = new Shape[4]; //创建Shape数组对象
shapes[0] = new Circle(1);
shapes[1] = new Circle(2); //大
shapes[2] = new Square(1);
shapes[3] = new Square(2);
maxArea(shapes);
}
//求数组的最大面积
public static void maxArea(Shape[] shapes){
double max = shapes[0].area();
int maxIndex = 0; //最大面积下标
for(int i=1;i<shapes.length;i++){
double area = shapes[i].area();
if(area>max){
max=area;
maxIndex=i;
}
}
System.out.println("最大面积为:"+max+",所在下标为:"+maxIndex);
}
}

abstract class Shape{ //抽象类
protected double c; //周长
public abstract double area(); //抽象方法
}
class Circle extends Shape{
public Circle(double c){
super.c = c;
}
public double area(){ //重写抽象方法
return 0.0796*c*c; //0.0625
}
}
class Square extends Shape{
public Square(double c){
super.c = c;
}
public double area(){
return 0.0625*c*c;
}
}

小梁丶的主页 小梁丶 | 初学一级 | 园豆:172
提问于:2017-04-19 01:09
< >
分享
最佳答案
0

把抽象类的抽象方法注释掉了,想要maxArea()里的.area()不报错,就得判断shapes[i]的具体类型,然后强转:double area = (Circle)shapes[i].area();else double area = (Square)shapes[i].area();如果子类比较多.....那就操蛋了。

抽象类抽象方法(具体类具体方法也行,只不过抽象类强制要求抽象方法子类必须重新实现,而具体类则由其子类选择是否重写父类方法)可以屏蔽这些细节。怎么做到呢:shapes[0]的引用类型是Shape,实际类型是Circle,编译的时候,如果area()不是静态方法,编译器会根据其实际类型(对象本身有内置指针指向其类型对象,保证了真实类型的准确性)调用实际类型的area()方法。

所以,父类抽象方法确实帮助实现了子类接口的统一,不管是Circle、Square还是其他什么子类,调用的时候统一(Shape)shapes[i].area();等具体编译的时候,自动调用各个具体子类的area()的实现方法

收获园豆:7
codingHeart | 小虾三级 |园豆:1511 | 2017-04-19 10:34

如果我注释掉了抽象方法,shapes[0]同样是Circle类型;为什么不能访问对象里的方法呢?

小梁丶 | 园豆:172 (初学一级) | 2017-04-19 21:56

@小梁丶: shapes[0]的引用类型是Shape(见代码:Shape[] shapes = new Shape[4]),你把抽象方法注释了,Shape的实例shapes[0]是不能调用类都没有定义的方法的。我知道你的意思,你既想父类不定义抽象方法,又想shapes[0]能调用area().看下面这个例子吧:

class Square extends Shape{
public Triangle(double c){
super.c = c;
}
public int points(){
return 3;
}
}

 

如果注释掉抽象方法,新增了三角形子类,shapes[i].area()是会有错的,但父类的抽象方法如果存在,会强制三角形取实现,就不会出现任何问题。

你的一切臆想都是建立在你知道实际类型和代码规模不大的基础上的,但设计者跟我们这些coder的思路不一样的,他需要考虑很多因素,假如shapes[i]是通过别人写的方法中返回的Shape类型(真实类型并不知道)或者通过反射添加的,你并不知道实际类型是什么,你怎么就能保证子类确实有area()方法可以调用呢,然而这些错误能通过更好的设计来避免,使得错误在编码阶段显现出来而不是运行时出错,设计者当然会采用这种更好的设计。

codingHeart | 园豆:1511 (小虾三级) | 2017-04-20 09:15

@codingHeart: 假如shapes[i]是通过别人写的方法中返回的Shape类型(真实类型并不知道)这句话不是很明白:能不能说得更清楚点.我目前理解不行

小梁丶 | 园豆:172 (初学一级) | 2017-04-22 15:25

@小梁丶:我写一个方法,方法返回一个shape类型的对象(具体类型可能是没有定义area方法的三角形类型,也可能是有area方法的圆类型),你调用之后得到返回值result,于是你继续你的代码,result.aera(),一旦result是三角形类型,那运行时就会出错(而通过抽象方法可以在编译阶段就提醒会出错)

codingHeart | 园豆:1511 (小虾三级) | 2017-04-22 19:34
其他回答(1)
0

你父类有的特性派生类肯定有,你子类有的特性父类不一定有。

因此你把父类的特性(方法)去掉,而你申明的又是父类的数组,编译器当然不知道你会塞个什么东西进去,所以会报错咯。

父类不能访问子类这个也正常,你子类可能有自己的特殊性,那么我父类去访问的时候怎么知道这些东西呢?而且在对象构造时子类的构造函数在执行时第一步就是去super父类,那么也就说明了现有父类后有子类,如果能够让父类去访问子类的信息这个就会出现问题(这个时候子类还没完成构造工作)。

shapes[0].area()这个具体是谁的area方法要看你shapes[0]里面是谁了,如果是Circle而且它又override掉了父类的这个方法那就是Circle的了,如果没有override那么就是父类的了。这个解释也很简单,主要是看shapes[0]在运行时是谁,这是面相对象语言本来的特性。

抽象方法是子类的统一入口这个描述不太准确,一个是方法,一个是对象实例,这两个没关系。抽象方法需要在其派生类中去实现,而抽象方法本身仅作为一种契约而已,这个不是这个方法的唯一入口,如果你直接Circle c=new Circle()后还是可以执行这个方法(这个被override方法本来就属于Circle对象)

收获园豆:3
Daniel Cai | 园豆:10424 (专家六级) | 2017-04-19 09:16

怎么判断:如果是Circle而且它又override掉了父类的这个方法那就是Circle的了;是引用吗?

支持(0) 反对(0) 小梁丶 | 园豆:172 (初学一级) | 2017-04-22 15:27

@小梁丶: 

针对这个场景吧。

Shape shape=new Circle();

shape.area();

这个在实际调用的时候area方法是个虚方法,那么需要根据对象派生关系找到实际调用方法的来源,由于你Circle已经override掉了此方法,而且此处实例化的实例是Circle,那么当然就调用Circle的方法了。

支持(0) 反对(0) Daniel Cai | 园豆:10424 (专家六级) | 2017-04-23 11:16
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册