在java中,我们可以将超类Object传递给子类引用吗?
我知道这是一个奇怪的问题/实际上不可行, 但我想了解这背后的逻辑 为什么在java中不允许这样做。
class Employee {
public void met1(){
System.out.println("met1");
}
}
class SalesPerson extends Employee
{
@Override
public void met1(){
System.out.println("new met1");
}
public void met2(){
System.out.println("met2");
}
}
public class ReferenceTest {
public static void main(String[] args) {
SalesPerson sales = new Employee(); // line 1
sales.met1(); // line 2
sales.met2(); // line 3
}
}
如果Java允许编译第1行,会发生什么? 问题出在哪里?
欢迎任何输入/链接。
答案 0 :(得分:20)
如果允许您的SalesPerson sales = new Employee();
语句进行编译,则会破坏Polymorphism的原则,这是该语言所具有的功能之一。
另外,您应该熟悉编译时类型和运行时类型的意思:
变量的编译时类型是它声明的类型,而运行时类型是变量指向的实际对象的类型。例如:
Employee sales = new SalesPerson();
编译时类型sales
为Employee
,运行时类型为SalesPerson
。
编译时类型定义了可以调用的方法,而运行时类型定义了实际调用期间发生的事情。
我们假设这句话有效:
SalesPerson sales = new Employee();
正如我所说,编译时类型定义了可以调用的方法,因此met2()
可以有资格进行调用。同时,Employee
类没有met2()
,因此实际调用是不可能的。
答案 1 :(得分:4)
没有。允许这样做是没有道理的。
原因是因为子类通常定义附加行为。如果您可以将超类对象分配给子类引用,那么当您尝试访问实际上不存在的类成员时,您将在运行时遇到问题。
例如,如果允许这样做:
String s = new Object();
你会遇到一些非常糟糕的问题。如果您尝试调用String
方法会发生什么?运行时会崩溃吗?或者也许会进行无操作?这应该编译吗?
如果运行时崩溃,您可以使用运行时检查来确保您收到的对象实际上包含您想要的方法。但是,您基本上实现了Java类型系统在编译时已经提供的保证。那真的是"功能"除了一堆你不应该首先编写的类型检查代码外,你只需付出代价。
如果执行no-ops而不是不存在的方法,那么当你想要访问的成员不存在时,确保你的程序按照写入运行是非常困难的,因为任何引用都可能是{任何时候{1}}。当您自己动手并控制所有代码时,这可能很容易处理,但是当您必须处理其他代码时,这些保证基本上就会消失。
如果你想让编译器进行检查,假设编译器编写者不会让你失望并且给你一个严厉的谈话 - 那么你就会回到"正常&#34 ;行为再一次。再说一次,这只是为了零利益而做的很多工作。
长话短说:不,这是不被允许的,因为这样做是没有道理的,如果语言设计师试图允许他们在他们可以做更多伤害之前被锁定。
答案 2 :(得分:3)
如果从类继承,则始终专注于超类的常见行为。
在您的示例中,SalesPerson
是一个特殊的Employee
。它继承了超类中的所有行为,并且可以覆盖行为以使其不同或添加新行为。
如果您允许,使用子类型的实例(如Employee e = new SalesPerson()
)初始化超类型的变量,那么您可以使用该变量上的所有常见行为。
如果相反,你可以反过来做,那么班上可能会有几个未初始化的成员。
在使用Java Collection API时经常会发现这种情况,例如,您可以在操作中使用常见的List
类,例如迭代它,但在初始化时,您可以使用例如子类{{ 1}}。