解析 <T extends Interface<T>> 并结束潜在的无限循环
Parsing <T extends Interface<T>> and ending a potential infinite cycle
考虑以
开头的 class 定义
class Pokemon extends Playable<Pokemon>
这与更常见的 class Pokemon implements Comparable<Pokemon>
类似,它只是对 Pokemon 施加了总排序。
虽然我看到这个和写这个已经有一段时间了,但我意识到(在回答一个问题之后)至少从理论的角度来看,如果不小心的话,解析中可能存在无限循环的风险.
考虑一下:
第 1 步:编译器或 classloader 尝试解析(或加载)Pokemon
,但发现它需要先解析 Playable<.>
。
第二步:编译器随后意识到因为Playable
被Pokemon
参数化了,需要加载或解析Pokemon
。现在我们发现自己进入了第 1 步,并且建立了一个永无止境的循环。
实际上,我们知道情况并非如此,因为它有效。那么循环是如何被打破的呢?我的理论是,在第 2 步结束时,编译器或 classloader 只是停止并使用 "reference" 到 Pokemon 而不是提取 Pokemon 源代码。但我对 javac
或 Classloaders
的了解还不够,无法证实这一点。有人可以称重吗?
这类似于此声明中的 "loop":
class LinkedListNode {
private String data;
private LinkedListNode next;
...
}
为了处理private LinkedListNode next
字段,编译器只需要知道LinkedListNode
是一个类型。此时不需要完整的类型,因为类型名称提供了足够的信息来声明该字段。
类似地,当您声明一个泛型 class 并引用其自身类型作为其继承结构的一部分时,编译器不需要完整的类型来完成对声明的解析。
考虑以
开头的 class 定义class Pokemon extends Playable<Pokemon>
这与更常见的 class Pokemon implements Comparable<Pokemon>
类似,它只是对 Pokemon 施加了总排序。
虽然我看到这个和写这个已经有一段时间了,但我意识到(在回答一个问题之后)至少从理论的角度来看,如果不小心的话,解析中可能存在无限循环的风险.
考虑一下:
第 1 步:编译器或 classloader 尝试解析(或加载)Pokemon
,但发现它需要先解析 Playable<.>
。
第二步:编译器随后意识到因为Playable
被Pokemon
参数化了,需要加载或解析Pokemon
。现在我们发现自己进入了第 1 步,并且建立了一个永无止境的循环。
实际上,我们知道情况并非如此,因为它有效。那么循环是如何被打破的呢?我的理论是,在第 2 步结束时,编译器或 classloader 只是停止并使用 "reference" 到 Pokemon 而不是提取 Pokemon 源代码。但我对 javac
或 Classloaders
的了解还不够,无法证实这一点。有人可以称重吗?
这类似于此声明中的 "loop":
class LinkedListNode {
private String data;
private LinkedListNode next;
...
}
为了处理private LinkedListNode next
字段,编译器只需要知道LinkedListNode
是一个类型。此时不需要完整的类型,因为类型名称提供了足够的信息来声明该字段。
类似地,当您声明一个泛型 class 并引用其自身类型作为其继承结构的一部分时,编译器不需要完整的类型来完成对声明的解析。