2017-03-02 183 views
0

考虑以下基本/派生类:从Java子类中调用的方法不是在父类

public class Car { 
    private int cylinders; 

    public Car(int cyl) { 
     cylinders = cyl; 
    } 

    public int getCylinders() { 
     return cylinders; 
    } 
} 

public class SportsCar extends Car { 
    private int topSpeed; 

    public SportsCar(int cyl, int top) { 
     super(cyl); 
     topSpeed = top; 
    } 

    public int getTopSpeed() { 
     return topSpeed; 
    } 
} 

现在,请考虑以下两个对象:

SportsCar lambo = new SportsCar(8,255); 
    Car m5 = new SportsCar(10,240); 

的以下方法调用编译正常:

lambo.getTopSpeed(); 

然而,随着错误调用此方法突破“无法找到符号 - 方法getTopSpeed()”

m5.getTopSpeed(); 

现在我明白getTopSpeed方法必须在基类中存在以便它因为m5编译是一个Car类型,所以如果我包含getTopSpeedCar那么m5.getTopSpeed();很好地编译。

我的问题是:

  1. 为什么错误“无法找到符号 - 方法getTopSpeed()”在编译时发生,而不是运行时间?
  2. 为什么“后期绑定”不能防止这个错误?
  3. 假设getTopSpeedCar实现(因此它编译)在程序运行时,不编译器首先检查以查看是否存在getTopSpeedCar,然后检查,看它是否超过深重在SportsCar还是只是“知道“它已经从编译器中被覆盖了,并直接使用了过度使用的方法?
+1

你的第一个问题,在此的其他问题的回答(http://stackoverflow.com/a/11466790/1079354)可以合理地第一句回答。其他的部分,我会鼓励对JLS的一些细读。 – Makoto

+1

'm5'是'Car'类型的引用变量,这意味着您只能调用'Car'中定义的方法。如果您想使用'SportsCar'中的方法,则必须将'm5'明确地转换为'SportsCar'。 – Logan

+0

编译器无法通过父类型的变量“查看”派生类型的成员。涉及到两种类型,编译器使用的_variable_的声明类型以及JVM使用的_object_的运行时类型。您期待编译器处理_variable_,就好像它可以看到_object_的运行时类型。它不能。 –

回答

1
  1. 的Java是静态类型语言,所以变量类型应该在编译时是已知的。如果编译器已知的类型不公开这种方法 - 这是编译失败。确切的说是不让它运行的原因。

  2. 为什么要这样呢?它只是发现方法体迟了,而不是签名。静态类型意味着签名必须在编译时满足。

  3. 运行时JVM试图找到最具体的实现。在SportsCar中,就你的情况而言。如果它是从父项继承的,但是在子项中不存在,则使用父代码。

如果您需要从具体的子这是变量类型未接电话的方法 - 可以在运行时在自己获得ClassCastException异常的风险投它。

+0

或者,如果您需要子类型,则只需将变量声明为子类型即可。铸造是代码气味。 –

+0

嗯,这取决于。是的,这增加了运行时异常的风险。但是,另一方面,如果你知道你在做什么 - 这可以。例如,对于消息类型,Akka/Java风格的模式匹配如何? – iTollu

+0

它仍然是代码味道。在绝大多数情况下,我已经看到'instanceof'的使用,这是为了解决类型分析中的一个空白。 –

1

这是因为当编译器看到这一点:

SportsCar lambo = new SportsCar(8,255); 
Car m5 = new SportsCar(10,240); 

它知道兰博是跑车,但只知道M5是一辆汽车。如果你想获得M5的最高速度,你必须把它转换为跑车第一:

((SportsCar)m5).getTopSpeed(); 

大多数我见过铸造,然而,该变量转换为另一个变量第一时间:

SportsCar sportsM5 = (SportsCar)m5; 
sportsM5.getTopSpeed(); 

This aspect of polymorphism in Java confused me once when I was passing an object to a method.