静态与动态绑定逻辑

我有以下代码:

import java.lang.*; public class Program { public static void main(String [] args) { B a = new A(); ap(10); ap(10.0); } } class B { public void p(double i) { System.out.println(i*2); } } class A extends B{ public void p(int i) { System.out.println(i); } } 

当我使用B a = new A()执行此代码时,在两种情况下都得到20.0这是有意义的,因为重载是编译期间的句柄,其中编译器查看声明的类型并适当地调用函数。 由于我们声明的类型是B类,因此在两种情况下都调用了B类的方法。 现在,如果我做A a = new A(); 我应该在两个答案中得到10分,但我不是。 对于ap(10)我得到10,对于ap(10.0) 20.0。 基于静态绑定的概念和通过静态绑定完成重载的整个概念,静态绑定查看声明的类型而不是实际类型,为什么结果会以这种方式出现? 我非常感谢你的帮助。

在你的情况下,你正在进行重载,它将在编译时被绑定(静态绑定。)。静态绑定发生在引用类型而不是引用所指向的对象类型。 在你的第一种情况下,你正在使用B的引用变量并为它分配一个A的对象。因为你的引用是B,即使你使用int,来自B的方法p(double)也会静态绑定(因为int可以是扩大到两倍)。

在第二种情况下,你使用引用作为A本身。在这种情况下,你有两个p()方法可用。一个是来自B的p(双)和来自A.的其他p(int)。所以p(10)将调用p (int)和p(10.0)将调用p(double)

尝试这个:

 class B { public void p(String i) { System.out.println("parent:"+i); } } class A extends B{ public void p(int i) { System.out.println(i); } } public class Test1 { public static void main(String args[]) { A a = new A(); //arg ap(10); ap("sample"); } } 

如果将标记为arg的行更改为B a = new A(),您将看到编译器在两种情况下都尝试调用父p。

int可以扩展为double ,但不是相反。 这意味着10可以调用Bp(double)Ap(int)10.0double ,不会被隐式转换为int即只调用Bp(double)

因为你的方法p不是一个被重写的方法,所以当你使用时它只是在你的子类中

 Super sup = new Sub(); sup.p(int); sup.p(double); 

在这种情况下,因为你的Super类有一个方法,它接受double作为参数,并且一个int可以适合 double你的Super-class方法被调用接受double的方法。

 Sub sup = new Sub(); sup.p(int); sup.p(double); 

但是,在这种情况下,由于你的子类没有一个带有double的方法,对于sup.p(double) call ,如果你传递double作为参数,它将使用超类中的inheritance方法。

当您编写A a = new A()您将创建一个类型为A的新对象,该对象将具有2个方法。 Ap(int)Bp(double) ,当你调用Ap(10.0) ,由于缺少转换,它将调用Bp(double)

这个反例可能会有所帮助:

 import java.lang.*; public class X { public static void main(String [] args) { B c = new A(); cp(10); cp(10.0); cp("AAA"); ((A)c).p(10); } } class B { public void p(String s) { System.out.println("B: my string is " + s); } public void p(double i) { System.out.println("B: twice my double is: " + i*2); } } class A extends B{ public void p(int i) { System.out.println("A: my number is " + i); } } 

输出:

 C:\temp>java X B: twice my double is: 20.0 B: twice my double is: 20.0 B: my string is AAA A: my number is 10 

问题是:

1)您将类型声明为“B”(不是“A”)

2)Bp(10) 可以接受int作为浮点参数

3)因此,这就是你得到的

这实际上是一个问题类型可以隐式转换的问题,而不是重载或重写的方法。

当对象声明了类型B ,调用double版本,因为它与int参数兼容,因为在Java中intdouble的子类型。

当对象声明为A ,它的方法p()重载了两个版本:

 p(int arg); p(double arg); 

因此,当你传递一个int ,第一个版本被选中,因为它更准确,当你传递第二个时,因为它是最具体的签名。

供参考,请参阅§15.12.2的相关JLS和Gilad Bracha的这篇文章 。 顺便说一句,不要试图根据你认为最合乎逻辑的方式弄清楚语言应该如何表现,因为每种编程语言都是一种工程努力,这意味着你付出的代价就是你付出的代价。 Java的主要信息来源是JLS,如果仔细阅读,你会(令人惊讶地发现)甚至会发现源代码中的行不明确且无法编译的情况。

为了使int的加宽效果更加生动,我创造了另一个值得一看的例子。 在这里,我创建了一个名为Parent的类,而不是int ,而不是int创建了一个Child类。

从而,
双〜父母
INT〜儿童

显然,子对象可以扩展为父引用。

 package test; public class OOPs { public static void main(String[] args) { Child ch = new Child(); // like int 10 Parent pa = new Parent();// like double 10.0 B a = new A(); // case 2 : A a = new A(); ap(ch);// 10 ap(pa);// 10.0 } } class B { public void p(Parent i) { System.out.println("print like 20"); System.out.println(i.getClass().getName()); } } class A extends B { public void p(Child i) { System.out.println("print like 10"); System.out.println(i.getClass().getName()); } } class Parent { String name; Parent() { name = "Parent"; } public String getName() { return name; } } class Child extends Parent { String name; Child() { name = "Child"; } public String getName() { return name; } } 

案例1 – 输出 (B a = new A();)
打印像20
test.Child
打印像20
test.Parent

案例2 – 输出 (A a = new A();)
打印像10
test.Child
打印像20
test.Parent