Java 中的绑定和调度有什么区别?

What is the difference between Binding and Dispatching in Java?

关联的名称太多:早期和晚期绑定、静态和动态调度、运行时与编译时多态性等,我不明白其中的区别。

我找到了一个清晰的explanation,但是它是正确的吗?我会解释 JustinC:

Binding:是判断一个变量(对象?)的类型。如果它是在编译时完成的,那么它就是早期绑定。如果在 运行 时间完成,则为后期绑定。

Dispatch: 正在确定哪个方法与方法调用匹配。静态调度是在编译时计算方法,而动态调度是在 运行 时间执行。

Binding 是否将原始变量和引用变量分别与原始值和对象匹配?

编辑:请给我一些明确的参考 material 以便我可以阅读更多相关信息。

这些是一般术语,您可以这样总结:当某些事物(方法或对象)是static/early时,表示该事物是在编译时配置的,并且在[=中没有歧义21=] 时间 例如在下面的代码中:

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 的输出,因此此方法是动态的 binded/dispatched(对于方法 link,我们可以使用术语分派而不是绑定)。

我认为混淆通常来自这些术语的超载。

我们用高级语言编写程序,编译器或解释器必须将其转换为机器真正理解的东西。

粗略地说,您可以想象一个编译器将我们的方法代码转换为某种形式的机器代码。如果编译器在那个时候确切地知道当我们稍后 运行 我们的程序时该方法在内存中的位置,那么它可以安全地找到这个编译方法的每个方法调用并将它替换为跳转到这个地址编译代码所在的位置,对吧?

好吧,具体化这种关系就是我所理解的约束力。但是,这种绑定可能发生在不同的时刻,例如在编译时、链接时、加载时,或者在 运行 时,具体取决于语言的设计。

术语静态和动态通常分别用于指代 运行 时间之前和 运行 时间之前绑定的事物。

较晚的绑定时间与更大的灵活性相关联,较早的绑定时间与更高的效率相关联。语言设计者在创建语言时必须平衡这两个方面。

大多数面向对象的编程语言都支持子类型多态性。在这些语言中,虚拟方法在 运行 时间绑定,具体取决于当时对象的动态类型。换句话说,虚方法调用在 运行 时根据所涉及的对象实现的动态类型分派到适当的实现,而不是仅基于其静态类型引用。

因此,在我看来,您必须首先将方法调用绑定到特定的实现或执行地址,然后才能向其调度调用。

had answered 过去有一个非常相似的问题,我在其中用示例演示了 Java 中这是如何发生的。

我还建议阅读这本书 Programming Language Pragmatics。从理论的角度来学习所有这些东西是一个很好的参考。

当您寻找 "low level" 定义时,唯一合法的来源可能是我们的老朋友 - JLS。虽然在这种情况下没有给出明确的定义,但它使用每个术语的上下文可能就足够了。

调度

在确定调用哪个方法的过程中确实提到了这个术语。

15.12.2. Compile-Time Step 2: Determine Method Signature

The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.

There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is the one used at run time to perform the method dispatch. A method is applicable if it is applicable by one of strict invocation

关于什么是 "most specific" 方法的详细说明在 15.12.2.5 选择最具体的方法.

中完成

至于"dynamic dispatch",

JLS 12.5. Creation of New Class Instances:

Unlike C++, the Java programming language does not specify altered rules for method dispatch during the creation of a new class instance. If methods are invoked that are overridden in subclasses in the object being initialized, then these overriding methods are used, even before the new object is completely initialized.

它包括

Example 12.5-2. Dynamic Dispatch During Instance Creation

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 中的方法,并且在字段被初始化。

绑定

此术语用于 class 成员访问的上下文。

Example 15.11.1-1. Static Binding for 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("s.x=" + s.x);
        System.out.println("s.x=" + s.z());
    }
}

输出:

s.x=0
s.x=1

显示字段使用"early binding",而实例方法使用"late binding":

This lack of dynamic lookup for field accesses allows programs to be run efficiently with straightforward implementations. The power of late binding and overriding is available, but only when instance methods are used.

绑定也用于确定泛型的类型,

8. Classes

Classes may be generic (§8.1.2), that is, they may declare type variables whose bindings may differ among different instances of the class.

意味着如果您创建 List<String> 的 2 个实例,则两个实例中 String 的绑定彼此不同。

这也适用于原始类型:

4.8. Raw Types

class Outer<T>{
    T t;
    class Inner {
        T setOuterT(T t1) { t = t1; return t; }
    }
}

The type of the member(s) of Inner depends on the type parameter of Outer. If Outer is raw, Inner must be treated as raw as well, as there is no valid binding for T.

意味着声明 Outer outer(这将生成原始类型警告)不允许确定 T 的类型(显然 - 它未在声明中定义)。