转载

Java 有 value type 吗?

有人看了我之前的文章『Swift 语言的设计错误』,问我:“你说 Java 只有 reference type,但是根据 Java 的 官方文档 ,Java 也有 value type 和 reference type 的区别的。” 现在我来解释一下这个问题。

Java 有 value type,其实是长久以来的一种误解,它混淆了实现和语义的区别。不要以为 Java 的官方文档那样写就是权威定论,就可以说“王垠不懂”哦。当你认为王垠对一个初级问题不懂的时候,都需要三思,他是不是大智若愚…… 看了我下面的论述,也许你会发现自己应该怀疑的是,Java 的设计者到底有没有搞明白这个问题 :P

胡扯结束,现在来说正事。Java,Scheme 等语言的原子类型,比如 char,int,boolean,double 等,在“实现”上确实是通过 value (而不是 reference,或者叫指针)直接传递的,然而这完全是为了效率的考虑。Java 继承了 Scheme 的衣钵,它们在“语义”上其实是没有 value type 的。

这不是天方夜谭,为了理解这一点,你可以做一个很有意思的思维实验:现在你把 Java 里面所有的原子类型都“想象”成 reference type,也就是说,所有的 int, boolean 等原子类型的变量都不包含实际的数据,而只是指向堆上数据的 reference(或者叫指针)。然后你会发现这样“改造后”的 Java,仍然符合所有你在 Java 代码里能够看到的现象。也就是说,原子类型被作为 value type 还是 reference type,对于程序员的思维活动完全没有区别!

举个简单的例子,如果我们把 int 的实现变成完全的 reference,然后来看这段代码:

int x = 1;  x = 2; 

由于改造后的 Java 里面 int 全部是 reference,所以 x 并不包含一个整数,而是一个 reference,它指向堆里分配的数据,这个堆空间的内容是整数 1。在第二行,我们对 x 这个 reference 赋值。你会发现一个很有意思的现象,虽然现在 int 成了 reference type,然而你并不能做你能对其它 reference type(比如 object)变量能做的任何特殊事情。你对 x 能做的唯一事情是对它进行赋值,使它指向另一个地方。

你没法改变 1 所在的堆内存地址里的值,因为在 Java 里,我们没法写像 C 语言的 *x = 2 这样的代码,因为 Java 没有提供 deref 操作符 * 。我们也没法改变 1 的一部分,因为它是一个原子类型。也就是说你没法使用像 x.foo = 2 这样的语句来改变数字 1 的“一部分”。最后你发现,其实你只能写 x = 2 这样的代码,也就是改变 x 这个 reference 本身的指向。然而 x = 2 这个语句只会把 x 改为指向堆里的另一个数字 2。原来数字 1 所在的内存空间并没有变成 2,只不过 x 现在指向了另一个地址,那个地址装着数字 2 而已!

注意到了吗?这也就是你能对 value type 的原子类型能做的所有事情。所以,不管 x 在实现上是 value type 还是 reference type,它们在语义上都是等价的。也就是说,原子类型是 value type 还是 reference type,对于程序员来说完全没有区别。你完全可以把 Java 所有的原子类型都想成 reference type,之后你能对它们做的事情,你的编程思路和方式,都不会因此有任何的改变。

从这个角度来看,Java 在语义上是没有 value type 的。value type 和 reference type 如果同时并存,程序员必须能够在语义上感觉得到它们的不同,然而不管原子类型是 value type 还是 reference type,作为程序员,你无法感觉到任何的不同。所以你完全可以认为 Java 只有 reference type,把原子类型全都当成 reference type 来用。实际上,所有的数据都是 reference type 就是 Scheme 和 Java 最初的设计原理。原子类型用 value 来传递数据只是一种性能优化,它对于程序员应该是透明(看不见)的。

原文  http://www.yinwang.org/blog-cn/2016/06/08/java-value-type
正文到此结束
Loading...