Java中的Binding和Dispatching有什么区别?
关联名称太多:早期和晚期绑定,静态和动态调度,运行时与编译时多态等等,我不明白其中的区别。
我找到了一个明确的解释 ,但它是否正确? 我会解释JustinC :
绑定:是确定变量的类型(对象?)。 如果它是在编译时完成的,那么它的早期绑定。 如果它是在运行时完成的,那就是后期绑定。
Dispatch:确定哪个方法与方法调用匹配。 Static Dispatch是编译时的计算方法,而动态调度是在运行时执行的。
绑定是否将原始值和引用变量分别与原始值和对象进行匹配?
编辑:请给我一些明确的参考资料,以便我可以阅读更多相关信息。
我认为这种混淆通常来自这些术语的过载。
我们用高级语言编写程序,编译器或解释器必须将其转换为机器实际理解的东西。
粗略地说,您可以想象编译器将我们的方法代码转换为某种forms的机器代码。 如果编译器在那时知道当我们稍后运行程序时该方法将驻留在内存中的确切位置,那么它可以安全地去找到这个编译方法的每个方法调用并将其替换为跳转到此地址的编译代码居住,对吗?
好吧,实现这种关系是我理解为具有约束力的。 但是,这种绑定可能发生在不同的时刻,例如在编译时,链接时间,加载时间或运行时,取决于语言的设计。
术语静态和动态通常分别用于指在运行时和运行时之前绑定的事物。
后来的绑定时间与更大的灵活性相关联,较早的绑定时间与更高的效率相关联。 语言设计师在创建语言时必须平衡这两个方面。
大多数面向对象的编程语言都支持子类型多态。 在这些语言中,虚拟方法在运行时绑定,具体取决于此时对象的动态类型。 换句话说,虚拟方法在运行时根据所涉及的对象实现的动态类型分派给适当的实现,而不仅仅基于其静态类型引用。
因此,在我看来,您必须首先将方法调用绑定到特定的实现或执行地址等,然后您可以调度它。
我在过去回答了一个非常类似的问题,在这个问题中我通过示例演示了Java中是如何发生这种情况的。
我还建议阅读“ 编程语言语用学 ”一书。 从理论的角度来学习所有这些东西是一个很好的参考。
这些是通用术语,您可以通过这种方式对其进行总结:当某些事物(方法或对象)是静态/早期时,它意味着事物在编译时配置,并且在运行时没有歧义,例如在以下代码中:
class A { void methodX() { System.out.print("i am A"); } }
如果我们创建一个A的实例并调用methodX(),那么没有什么是雄心勃勃的,并且每个都在编译时配置但是如果我们有以下代码
class B extends A { void methodX() { System.out.print("i am B"); } } .... A objX= new B(); objX.methodX();
方法x的输出直到运行时才知道,因此这个方法是动态绑定/分派的(我们可以使用术语dispatched而不是bind for methods 链接 )。
当你在寻找“低级”定义时,可能唯一的合法来源是我们的老朋友–JLS。 虽然在这种情况下它没有给出明确的定义,但它使用每个术语的上下文可能就足够了。
调度
在确定调用哪种方法的过程中确实提到了这个术语。
15.12.2。 编译时间步骤2:确定方法签名
第二步搜索上一步中为成员方法确定的类型。 此步骤使用方法名称和参数表达式来查找可访问和适用的方法,即可以在给定参数上正确调用的声明。
可能存在多于一种这样的方法,在这种情况下,选择最具体的方法。 最具体方法的描述符(签名加返回类型)是在运行时用于执行方法分派的方法。 如果通过严格调用之一适用,则该方法适用
关于什么是“最具体”方法的详细说明在15.12.2.5选择最具体的方法中完成 。
至于“动态派遣”,
JLS 12.5。 创建新的类实例 :
与C ++不同,Java编程语言在创建新类实例期间未指定方法分派的更改规则。 如果调用在正在初始化的对象的子类中重写的方法,则使用这些重写方法,即使在新对象完全初始化之前也是如此。
这包括
例12.5-2。 实例创建期间的动态调度
class Super { Super() { printThree(); } void printThree() { System.out.println("three"); } } class Test extends Super { int three = 3; void printThree() { System.out.println(three); } public static void main(String[] args) { Test t = new Test(); t.printThree(); } }
输出:
0
3
这是因为在构造函数调用链期间, Super
的构造函数调用printThree
,但由于动态调度, Test
的方法被调用,即在字段初始化之前。
捆绑
该术语用于类成员访问的上下文中。
例15.11.1-1。 Field Access的静态绑定演示了早期和晚期绑定。 我将总结那些给我们懒惰的例子:
class S { int x = 0; int z() { return x; } } class T extends S { int x = 1; int z() { return x; } } public class Test1 { public static void main(String[] args) { S s = new T(); System.out.println("sx=" + sx); System.out.println("sx=" + sz()); } }
输出:
SX = 0
SX = 1
显示该字段使用“早期绑定”,而实例方法使用“后期绑定”:
缺乏对字段访问的动态查找允许程序通过简单的实现有效地运行。 可以使用后期绑定和覆盖的function,但仅限于使用实例方法时。
绑定也用于确定generics的类型,
8.课程
类可以是通用的 (第8.1.2节),也就是说,它们可以声明类型变量,其绑定可能在类的不同实例之间不同。
这意味着如果创建List
2个实例,则两个实例中String
的绑定彼此不同。
这也适用于原始类型:
4.8。 原始类型
class Outer
{ T t; class Inner { T setOuterT(T t1) { t = t1; return t; } } } Inner的成员类型取决于Outer的类型参数。 如果Outer是raw,那么Inner也必须被视为raw,因为T没有有效的绑定。
这意味着声明Outer outer
(这将生成原始类型警告)不允许确定T
的类型(显然 – 它没有在声明中定义)。