Ste*_* P. 7 java overriding overloading
请原谅我,如果这个问题主要是基于意见的,但我觉得它不是,而且选择的理由很充分.所以,这是一个例子.对不起,它真的很长,但超级简单:
接口:
public interface Shape
{
double area ();
}
Run Code Online (Sandbox Code Playgroud)
实施第1课:
import static java.lang.Math.PI;
public class Circle implements Shape
{
private double radius;
public Circle(double radius)
{
this.radius = radius;
}
public double area()
{
return PI*radius*radius;
}
}
Run Code Online (Sandbox Code Playgroud)
实施第2课:
public class Square implements Shape
{
private double size;
public Square(double sideLength)
{
size = sideLength;
}
public double area()
{
return size*size;
}
}
Run Code Online (Sandbox Code Playgroud)
司机:
Shape[] shapes = new Shape[]{new Circle (5.3), new Square (2.4)};
System.out.println(shapes[0].area()); //prints 88.247...
System.out.println(shapes[1].area()); //prints 5.76
Run Code Online (Sandbox Code Playgroud)
这是因为.area()被Circle和重写了Square.现在,这是我的问题真正开始的地方.假设驱动程序有以下方法:
public static void whatIs(Shape s)
{
System.out.println("Shape");
}
public static void whatIs(Circle s)
{
System.out.println("Circle");
}
public static void whatIs(Square s)
{
System.out.println("Square");
}
Run Code Online (Sandbox Code Playgroud)
如果我们打电话:
whatIs(shapes[0]); //prints "Shape"
whatIs(shapes[1]); //prints "Shape"
Run Code Online (Sandbox Code Playgroud)
发生这种情况是因为Java将对象解释为Shapes而不是Circle和Square.当然,我们可以通过以下方式获得预期结果:
if (shapes[0] instanceof Circle)
{
whatIs((Circle) shapes[0]); //prints "Circle"
}
if (shapes[1] instanceof Square)
{
whatIs((Square) shapes[1]); //prints "Square"
}
Run Code Online (Sandbox Code Playgroud)
现在我们有了一个背景,我的问题是:
编译器/语言设计有哪些原因whatIs(shapes[0]);会打印出"Shape?" 同样,为什么Java编译器能够准确区分相关对象的重写方法,而不是重载方法?更具体地说,如果驱动程序可以访问的唯一方法是:
public static void whatIs(Circle s)
{
System.out.println("Circle");
}
public static void whatIs(Square s)
{
System.out.println("Square");
}
Run Code Online (Sandbox Code Playgroud)
我们试图打电话,
whatIs(shapes[0]);
whatIs(shapes[1]);
Run Code Online (Sandbox Code Playgroud)
我们将得到两个错误(一个用于Square,一个用于Circle),表明:
- 方法Driver.whatIs(Square)不适用
- 实际参数Shape不能通过方法调用转换转换为Square
那么,再说一遍,既然我们已经掌握了这个细节,为什么Java不能处理这样的情况呢?如果由于效率问题而完成,是否由于某些设计决定而无法实现,这是不是出于某种原因的不良做法等等?
具有面向对象特性的Java支持多态,因此调用area将调用area特定实例的方法,无论它是什么.这是在运行时确定的.
但是,重载方法不支持此多态性.在Java语言规范,第8.4.9涵盖这样的:
调用方法时(第15.12节),在编译时使用实际参数的数量(以及任何显式类型参数)和参数的编译时类型来确定将被调用的方法的签名( §15.12.2).如果要调用的方法是实例方法,则将在运行时使用动态方法查找(第15.12.4节)确定要调用的实际方法.
也就是说,使用重载方法,在编译时使用变量的编译时类型选择方法,而不是像运行时那样使用多态.
为什么 Java 编译器可以准确区分相关对象的重写方法,而不能准确区分重载方法?
它不能。
它严格按照它可以看到和保证的类型进行检查。如果您的代码是shapes[0].area(),它将检查该方法是否Shape具有area方法并将其编译为“在该对象上调用 area()”。现在保证运行时存在的具体对象具有该方法。实际使用哪个类的哪个版本在运行时动态解析。
调用重载方法的工作原理相同。编译器看到 aShape并将其编译为“在基本形状版本中调用 whatis()”。如果你想改变它(甚至允许没有基本Shape版本),你需要能够在编译时确定类型。
但是,AFAIK 不可能创建一个编译器来确定对象在运行时将具有的类型。想想例如:
final Shape[] shapes = new Shape[] { new Circle(5.3), new Square(2.4) };
new Thread() {
public void run() {
shapes[0] = new Square(1.5);
}
}.start();
whatIs(shapes[0]);
Run Code Online (Sandbox Code Playgroud)
您必须执行该代码才能找到答案。
编译器可以自动生成代码,如
if (shapes[0] instanceof Circle)
{
whatIs((Circle) shapes[0]); //prints "Circle"
}
Run Code Online (Sandbox Code Playgroud)
为您在运行时实现动态方法调用,但它没有。我不知道原因,但有时会很好。虽然instanceof这通常是糟糕的类设计的标志——你不应该从外部寻找差异,让类的行为不同,这样外部就不需要知道。