静态与动态绑定逻辑
我有以下代码:
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.0
是double
,不会被隐式转换为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中int
是double
的子类型。
当对象声明为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