为什么 StructuredArray 需要是不可构造的?
Why does StructuredArray need to be non-constructible?
This talk 在 34:00 描述了 Java 的 StructuredArray
的设计。一切都相当清楚,除了以下几点:
它不应该是可构造的,即实例可能只能通过某些 静态工厂方法 获得,例如 newInstance。同时,它们应该是可子类化的,这意味着必须有一个 public 构造函数,并且在运行时将保证不可构造性。这听起来很老套,所以我想知道 为什么?
我了解工厂的一般优势,尤其是静态工厂方法。但是我们在这里得到了什么,以便让黑客可以接受?
语义 浏览 API 文档,我的理解是这主要是语义问题。并提供 Fluent API。此外,如果您转到演示文稿的结论幻灯片,您应该注意到语义项目符号排在第一位(如果我们不计算源代码 url)。
如果我们选择普通数组。它们呈现出清晰的语义:
- 数组类型
- 数组长度
- 元素类型
结果
我们有一个统一的数组处理模型。而 API 是 crystal 清楚的。没有 10 种不同的方式来处理数组。我相信对于 Java 语言开发人员来说,api 的这种简洁性极其重要。强迫不可构造性,他们隐含地强迫我们按照他们希望我们使用它的方式使用 API 。
施工
因为 StructuredArray 本质上也是数组。提供构造函数会立即迫使我们使用 StructuredArray 的具体实现,这会自动产生问题,引入这种 "What exactly is an "Array?".
的统一模型
这就是为什么通过 Java 文档我们可以看到 StructuredArray 的实际构造方式:
static <S extends StructuredArray<T>,T> S newInstance(java.lang.invoke.MethodHandles.Lookup lookup,
java.lang.Class<S> arrayClass,
java.lang.Class<T> elementClass,
java.util.Collection<T> sourceCollection)
这里可见的是 StructuredArray 强制执行几项操作:
- 它强制所有客户端 classes 使用 "StructuredArray" 而不是具体实现。
- StructuredArray 本质上是不可变的。
- 不变性意味着长度有严格的表示法。
- 结构化数组有一个元素源。一旦消耗掉就可以处理掉。
- 与常规数组类似,结构化数组具有元素类型的概念。
我相信有很强的语义符号,而且作者也给了我们一个很好的提示,告诉我们编码应该如何发生。
结构化数组的另一个有趣的特性是能够传递构造函数。我们再次谈论接口和 API 与实际实现的强解耦。
数组模型
我的话通过检查 StructuredArrayModel 得到进一步证实
http://objectlayout.github.io/ObjectLayout/JavaDoc/index.html?org/ObjectLayout/StructuredArray.html
StructuredArrayModel(java.lang.Class<S> arrayClass, java.lang.Class<T> elementClass, long length)
从构造函数中可以看到三件事:
- 数组 class
- 元素的类型
- 长度
进一步观察结构化数组支持的结构:
An array of structs:
struct foo[];
A struct with a struct inside:
struct foo { int a; bar b; int c; };
A struct with an array at the end:
struct foo { int len; char[] payload; };
StructuredArrayModel 完全支持它
与 StructuredArray 相比,我们能够轻松实例化模型的具体实现。
StructuredArray 向我们展示了传递伪构造函数的能力 http://objectlayout.github.io/ObjectLayout/JavaDoc/org/ObjectLayout/CtorAndArgs.html
newInstance(CtorAndArgs<S> arrayCtorAndArgs, java.lang.Class<T> elementClass, long length)
StructuredArray
class 的要点是有一天它可以被一个内部实现替换,该实现将整个数组(包括组件对象)分配为一个长内存块。 发生这种情况时,对象的大小将取决于元素的数量和元素class。
如果 StructuredArray
有一个 public 构造器,那么你可以写 x = new StructuredArray<>(StructuredArray.class, MyElement.class, length)
。这似乎没有任何问题,除了在 bytecode 中,这变成了分配对象的 new
指令,然后是 separate invokespecial
指令调用对象的构造函数。
你看到问题了——new
指令必须分配对象,但它不能,因为对象的大小取决于构造函数参数(它没有的元素 class 和长度)!直到稍后某个时候的构造函数调用才传递这些。
有很多方法可以解决这样的问题,但它们都有点恶心。将构造封装在静态工厂方法中更有意义,因为这样你就 不能写 new StructuredArray...
,而且 JVM 不必使用任何 "magic" 来计算在 new
指令中为 StructuredArray
分配多少内存,因为不可能有任何这样的指令*。
如果以后的某些 JVM 想要提供分配连续数组的静态工厂的内部实现,那没问题——它在工厂方法调用中获得所需的所有信息。
注意* - 是的,好的,从技术上讲,您 可以 编写 new StructuredArray...
,但它对您来说不是一个有用的对象。
This talk 在 34:00 描述了 Java 的 StructuredArray
的设计。一切都相当清楚,除了以下几点:
它不应该是可构造的,即实例可能只能通过某些 静态工厂方法 获得,例如 newInstance。同时,它们应该是可子类化的,这意味着必须有一个 public 构造函数,并且在运行时将保证不可构造性。这听起来很老套,所以我想知道 为什么?
我了解工厂的一般优势,尤其是静态工厂方法。但是我们在这里得到了什么,以便让黑客可以接受?
语义 浏览 API 文档,我的理解是这主要是语义问题。并提供 Fluent API。此外,如果您转到演示文稿的结论幻灯片,您应该注意到语义项目符号排在第一位(如果我们不计算源代码 url)。
如果我们选择普通数组。它们呈现出清晰的语义:
- 数组类型
- 数组长度
- 元素类型
结果
我们有一个统一的数组处理模型。而 API 是 crystal 清楚的。没有 10 种不同的方式来处理数组。我相信对于 Java 语言开发人员来说,api 的这种简洁性极其重要。强迫不可构造性,他们隐含地强迫我们按照他们希望我们使用它的方式使用 API 。
施工
因为 StructuredArray 本质上也是数组。提供构造函数会立即迫使我们使用 StructuredArray 的具体实现,这会自动产生问题,引入这种 "What exactly is an "Array?".
的统一模型这就是为什么通过 Java 文档我们可以看到 StructuredArray 的实际构造方式:
static <S extends StructuredArray<T>,T> S newInstance(java.lang.invoke.MethodHandles.Lookup lookup,
java.lang.Class<S> arrayClass,
java.lang.Class<T> elementClass,
java.util.Collection<T> sourceCollection)
这里可见的是 StructuredArray 强制执行几项操作:
- 它强制所有客户端 classes 使用 "StructuredArray" 而不是具体实现。
- StructuredArray 本质上是不可变的。
- 不变性意味着长度有严格的表示法。
- 结构化数组有一个元素源。一旦消耗掉就可以处理掉。
- 与常规数组类似,结构化数组具有元素类型的概念。
我相信有很强的语义符号,而且作者也给了我们一个很好的提示,告诉我们编码应该如何发生。
结构化数组的另一个有趣的特性是能够传递构造函数。我们再次谈论接口和 API 与实际实现的强解耦。
数组模型
我的话通过检查 StructuredArrayModel 得到进一步证实 http://objectlayout.github.io/ObjectLayout/JavaDoc/index.html?org/ObjectLayout/StructuredArray.html
StructuredArrayModel(java.lang.Class<S> arrayClass, java.lang.Class<T> elementClass, long length)
从构造函数中可以看到三件事: - 数组 class - 元素的类型 - 长度
进一步观察结构化数组支持的结构:
An array of structs: struct foo[]; A struct with a struct inside: struct foo { int a; bar b; int c; }; A struct with an array at the end: struct foo { int len; char[] payload; };
StructuredArrayModel 完全支持它 与 StructuredArray 相比,我们能够轻松实例化模型的具体实现。
StructuredArray 向我们展示了传递伪构造函数的能力 http://objectlayout.github.io/ObjectLayout/JavaDoc/org/ObjectLayout/CtorAndArgs.html
newInstance(CtorAndArgs<S> arrayCtorAndArgs, java.lang.Class<T> elementClass, long length)
StructuredArray
class 的要点是有一天它可以被一个内部实现替换,该实现将整个数组(包括组件对象)分配为一个长内存块。 发生这种情况时,对象的大小将取决于元素的数量和元素class。
如果 StructuredArray
有一个 public 构造器,那么你可以写 x = new StructuredArray<>(StructuredArray.class, MyElement.class, length)
。这似乎没有任何问题,除了在 bytecode 中,这变成了分配对象的 new
指令,然后是 separate invokespecial
指令调用对象的构造函数。
你看到问题了——new
指令必须分配对象,但它不能,因为对象的大小取决于构造函数参数(它没有的元素 class 和长度)!直到稍后某个时候的构造函数调用才传递这些。
有很多方法可以解决这样的问题,但它们都有点恶心。将构造封装在静态工厂方法中更有意义,因为这样你就 不能写 new StructuredArray...
,而且 JVM 不必使用任何 "magic" 来计算在 new
指令中为 StructuredArray
分配多少内存,因为不可能有任何这样的指令*。
如果以后的某些 JVM 想要提供分配连续数组的静态工厂的内部实现,那没问题——它在工厂方法调用中获得所需的所有信息。
注意* - 是的,好的,从技术上讲,您 可以 编写 new StructuredArray...
,但它对您来说不是一个有用的对象。