Parent p = new GrandChild(); 和GrandChild p = new GrandChild(); /l
区别,深度分析
(假设类继承链:Parent → Child → GrandChild
):
Parent p = new GrandChild();
Parent
(编译器只认Parent
的成员)GrandChild
(实际执行时按GrandChild
行为工作)GrandChild p = new GrandChild();
Java 的“声明类型”是把一部分单元测试提前到了编译期,用“约束”换“安全”。
Parent p = new GrandChild(); 就是显式告诉编译器:“请把我当成 Parent 来检查,逼我只用 Parent 的契约”。
这里的“安全”指的是编译期就能拦截、永远不会拖到运行时才爆炸的一类错误。
具体说,就是 “你调用了不存在的方法/字段,或者把对象塞进了不兼容的容器”——这类错误在 Python 里往往要等到单元测试甚至线上流量跑起来才被发现,而 Java 的声明类型可以在按下 javac 回车时就直接拒绝。
Parent p = new Child(); // 实际是 Child
GrandChild g = (GrandChild)p; // 编译器允许语法,但运行期抛 ClassCastException
虽然这里仍可能在运行期失败,但强转语法本身就是显式风险声明;编译器会强制你必须写 (GrandChild),让你意识到“这一步可能炸”。
没有声明类型时,这种错误可能在 Python 里通过鸭子类型潜伏得更深。
. 先分清两个“我”是谁

2. “把我当成 Parent 来检查”——编译器眼里只有左边的“我”
‘’’’
Parent p = new GrandChild();
‘’’’
• 编译器看到变量 p 的静态类型是 Parent。
• 从此以后,只要源码里出现 p.XXX,编译器只会去 Parent.class 里找 XXX。
◦ 找到 → 通过。
◦ 找不到 → 直接报编译错误,哪怕 GrandChild 里真有这个方法。
• 这就等于告诉编译器:
“别管右边实际是什么,你就当它是 Parent。”
“逼我只用 Parent 的契约”——对写代码的人施加约束
• 契约 = Parent 公开的所有 public/protected 方法 + 字段 + 约定俗成的语义。
• 因为你只能看到 Parent 里声明的成员:
◦ 你无法直接写 p.childOnlyMethod() —— 编译期就拦下。
◦ 你也无法直接访问 p.childField —— 同样拦下。
• 于是调用者被迫只依赖 Parent 的“最小承诺”,不会意外耦合到 GrandChild 的额外细节。
◦ 将来把 new GrandChild() 换成 new Child()、new AnotherChild(),只要它们都是 Parent 的子类,调用方代码一行都不用改
只要把“契约”拆成两层看就通了: