【问题标题】:Object initialisation - The relation of the constructor to class type and method access对象初始化 - 构造函数与类类型和方法访问的关系
【发布时间】:2019-10-14 08:23:41
【问题描述】:

我有以下代码:

package testapp;

class Calculation {

    public Calculation(){}

    public void addition(int x, int y) {   }

    public void Subtraction(int x, int y) {   }
}

class My_Calculation extends Calculation {

    public My_Calculation(){}

    public void multiplication(int x, int y) {   }
}

public class TestApp {

    public static void main(String[] args) {

        int a = 20, b = 10;

        My_Calculation demo = new My_Calculation();
        demo.addition(a, b);
        demo.Subtraction(a, b);
        demo.multiplication(a, b);
        System.out.println(demo.getClass().getName());

        Calculation d = new Calculation();
        d.Subtraction(b, b);
        d.addition(b, b);
        // no multiplication
        System.out.println(d.getClass().getName());

        Calculation d2 = new My_Calculation();
        d2.Subtraction(b, b);
        d2.addition(b, b);
        // no multiplication
        System.out.println(d2.getClass().getName());
    } 
}

输出是这样的:

demo = testapp.My_Calculation

d = testapp.Calculation

d2 = testapp.My_Calculation

以下语句是一个声明,将变量名称 d2 引用到对象类型 Calculation

Calculation d2;

以下语句是调用 My_Calculation 构造函数的初始化。

Calculation d2 = new My_Calculation();

当我运行以下代码时,输​​出表明 d2 属于 My_Calculation 类类型,但可以访问 Calculation 类 中的方法.

System.out.println(d2.getClass().getName());

输出:testapp.My_Calculation

访问:加法;减法

现在,我的理解告诉我,如果调用 My_Calculation 构造函数,我应该:

1.只能使用乘法,或

2。访问加法、减法和乘法。

但是,我实际上得到了相反的结果:只能访问加法和减法。因此,我认为这是违反直觉的。

有人可以向我解释这里发生的事情,让我对为什么 type: My_Calculation 的对象无法访问其自己的方法,而是访问只有超类方法

【问题讨论】:

  • 设身处地为编译器着想:你需要编译一个方法foo(Calculation c) { ... }。在这种方法中接受对c.multiplication() 的调用是否安全?
  • 无关:阅读有关 java 命名约定的信息。方法名称总是使用 camelCase(),并且它们应该包含一个动词,例如 multiply() 。并且:不要在正常名称中使用 _,它们只会进入 SOME_CONSTANT。

标签: java methods constructor initialization new-operator


【解决方案1】:

这里:

Calculation d2 = new My_Calculation();

您创建了一个My_Calculation 类型的对象,但您将它分配给了一个使用超类型声明的变量!而且编译器不够聪明,无法“看到”d2 实际上是子类型。因此,编译器 会阻止您调用在该子类型上定义的方法。因为对于编译器来说,d2 被称为Calculation

请注意,您可以强制转换来告诉编译器“我知道得更好”:

( (My_Calculation) d2) ).multiplication(...

这在编译时和运行时都可以工作!

你的误解从这里开始:

Calculation d2 = new My_Calculation();

你在作业的右手边所做的基本上是在下一行“忘记”了。然后编译器只有知道你有一些d2 类型为Calculation

最后:是的,在您的特定示例中,编译器可以轻松确定d2真实类型。但在很多情况下,这并不容易,甚至是不可能的。因此,Java 背后的人决定忽略这些潜在的“知识”。

【讨论】:

    【解决方案2】:

    有人可以向我解释这里发生的事情吗? 对类型对象的连贯理解:My_Calculation 会 无法访问自己的方法,只能访问超类 方法。

    因为从编译器的角度来看,这不是访问该方法的对象,而是引用该方法的变量。
    如果你声明:

    Calculation d2 = new My_Calculation();
    

    或者对象引用,如果你没有声明任何变量,例如:

    new My_Calculation().doMethod();
    

    编译器将d2 视为Calculation,因此允许执行此类提供的操作。
    这种方式称为接口/超类型编程。这意味着您可以通过实例化类型中的任何实现,并且代码将始终有效。
    例如这个变化:

    Calculation d2 = new MyCalculationOtherSubclass();
    

    如果MyCalculationOtherSubclassCalculation 的另一个子类,则整个代码都可以编译。

    如果您需要使用特定的子类型,声明子类型是有意义的:

    My_Calculation d2 = new My_Calculation();
    

    或者如此沮丧:((My_Calculation)d2).invokeSubclassMethod();.
    同样StringCharSequence 的子类,但如果你想使用subString() 特定于String,你需要操作String: 所以你要声明它:String aString = "..."; 而不是CharSequence aString = "...";

    【讨论】:

      【解决方案3】:

      这是因为,在编译时,您只能访问您正在处理的对象的静态类型(即变量的类型)以及来自其超类、接口和超接口的方法。 getClass 方法是可用的,因为它是在Object 类中定义的,并且Calculation 继承自它。因此,如果你想调用multiplication,你必须将你的变量声明为My_Calculation

      然而,在运行时,被调用的方法是来自动态类型的方法(您分配给变量的方法)。因此,如果您在My_Calculation 中覆盖方法addition,即使静态类型是Calculation,该方法也将是在运行时调用的方法。

      【讨论】:

        猜你喜欢
        • 2021-11-06
        • 2013-12-30
        • 2012-11-05
        • 1970-01-01
        • 2015-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-24
        相关资源
        最近更新 更多