为什么Java编译器决定是否可以基于“;参考”;类型,而不是实际的“;对象”;类型?

为什么Java编译器决定是否可以基于“;参考”;类型,而不是实际的“;对象”;类型?,java,Java,我只是想知道为什么Java编译器决定是否可以基于“引用”类型而不是实际的“对象”类型调用方法?为了解释,我想举一个例子: class A { void methA() { System.out.println("Method of Class A."); } } class B extends A { void methB() { System.out.println("Method of Class B."); } public static void main(Strin

我只是想知道为什么Java编译器决定是否可以基于“引用”类型而不是实际的“对象”类型调用方法?为了解释,我想举一个例子:

class A {
void methA() {
    System.out.println("Method of Class A.");
} 
}

class B extends A {
void methB() {
    System.out.println("Method of Class B.");
}
public static void main(String arg[]) {
    A ob = new B();
    ob.methB();       // Compile Time Error
}
}

这将产生编译时错误,方法
methB()
类a
中找不到,尽管对象引用“ob”包含由方法
methB()
组成的
类B
的对象。原因是Java编译器检查
类A
(引用类型)中的方法,而不是
类B
(实际对象类型)中的方法。所以,我想知道这背后的原因是什么。为什么Java编译器在类A中查找方法?为什么不在类B(实际对象类型)中查找方法?

通过将变量声明为类型A,实际上隐藏了它实际上是一个子类型的事实。看不到类型B的方法是标准行为,这在(严格类型化的)面向对象中是必不可少的。

假设您有一个
动物类和一个扩展
动物类的
狗类。现在,如果动物定义了一个名为
speak()
的方法,而狗定义了一个名为
bark()
的方法。如果您这样做:

Animal a = new Dog();
意思是你指着一只狗说它是一种动物。当您将狗视为动物(而不是狗)时,只能调用为动物而不是狗定义的方法


在编译过程中,编译器会检查所调用的方法是否在引用类型中定义。

您的疑问是可以的,但这是一种oops类型。您正在将一个子类对象分配给超类,所以我将只向调用者公开超类的方法


简单地假设一个类作为接口,它有两个方法,而B是实现类,其中有十个方法。但如果您尝试使用接口A为该类创建对象,它将只公开2个方法。同样的逻辑也适用于您的情况。

很简单,编译器不知道变量的类型。与上面描述的场景不同,实际的“对象”类型通常不是很明显。这就是为什么“对象”类型是运行时类型:它在运行时是已知的

想象这样一种方法:

public static void process(A ob) {
    ob.methB(); //how do you know if this is valid?
}

public static void main(String[] args) {
    process(new B()); //would make the above call valid
    process(new A()); //would make the above call invalid
}
简单地说,您允许变量具有使调用无效的运行时类型。它可能是有效的,但如果依赖于
methB()
,则必须将B作为参数

回到您的代码示例,这个示例没有任何意义:

A ob = new B();
//why would you declare ob as A if you are storing a B inside it
//this is only useful if you want to store different types of objects in it at some point
//however, that's not the case, because in the next line:
ob.methB(); //you're calling methB()

由于对象的传递方式多种多样,编译器不可能知道在给定点对象将是什么类型。如果你,程序员,确实知道,你可以在适当的时候施放它。如果您不确定,您可以在任何时候使用instanceof进行检查,因为这是语言的设计方式。看来只有一种情况是编译器明确知道对象类型,并且没有将其做成特例。如果你想让编译器知道它是一个B,就把它声明为一个B。