当您在寻找“低级”定义时,唯一合法的来源可能是我们的老朋友——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
调用了 in 的方法,也就是在字段初始化之前。
捆绑
该术语用于类成员访问的上下文中。
示例 15.11.1-1。字段访问的静态绑定演示了早期和晚期绑定。我将总结那里为我们懒惰的人提供的示例:
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());
}
}
输出:
sx=0
sx=1
显示该字段使用“早期绑定”,而实例方法使用“后期绑定”:
这种对字段访问的动态查找的缺乏允许程序通过简单的实现有效地运行。后期绑定和覆盖的功能是可用的,但只有在使用实例方法时才可用。
绑定也用于确定泛型的类型,
8. 课程
类可以是泛型的(第 8.1.2 节),也就是说,它们可以声明类型变量,其绑定在类的不同实例中可能不同。
这意味着如果您创建 2 个实例List<String>
,则两个实例中的绑定String
彼此不同。
这也适用于原始类型:
4.8. 原始类型
class Outer<T>{
T t;
class Inner {
T setOuterT(T t1) { t = t1; return t; }
}
}
Inner 的成员类型取决于 Outer 的类型参数。如果 Outer 是原始的,则 Inner 也必须被视为原始的,因为 T 没有有效的绑定。
这意味着声明Outer outer
(这将生成原始类型警告)不允许确定T
(显然 - 它没有在声明中定义)的类型。