与Neal Gafter探讨Java的未来

五月底,我在巴黎中心的“Le Grand Rex”参加了令人振奋的“What’s Next”会议。 两场基调演讲之一的演讲者是Neal Gafter,他是Java SE 4和5语言增强的主要设计者和实现者,目前就职于微软的.NET平台语言团队。我很幸运的得到代表InfoQ采访他的机会,以下是对话的文字记录。在他的 演讲中,Neal表达了他对Oracle收购Sun微系统公司的看法:总体来说是件好事,“有人领导下的创新工作会更好地开展下去”,所以在采访一开始, 我便就Oracle接手Java社区一事询问了他的观点。

Neal Gafer:嗯,我想对于Oracle的收购来说,社区是最不乐观的方面了吧。

我想他们还在做很多事情。我是指那些他们一直说在做,但其实还没做到的事情。他们想用开放的方式来开发语言规范。他们保证过,说至少从现在起JSR都会在开放环境下操作,所有专家组的邮件列表都是公开可读的。

然后他们发起了一些JSR,例如Project Coin和Progject Lambda。然后就这么进行下去了,他们会提供公开的审查,但是那些专家讨论组依然不公开。

我对他们说,只要我能看到那些专家组的讨论,我就很乐意为你们提供详细的反馈作为参考。因为这样做的话,许多讨论的基本方式就会公开无疑,就能在专家组讨论的环境里进行理解了。

我得到的答复是:“我们正在努力,到时候会联系你的”,专家组倒也没有直接说“我们不想开放”。专家组很乐意开放讨论内容,Oracle也说他们希望开放,但现实就是:还没这么做。

因此,公开评审阶段取消了,到现在还访问不了。他们正在处理的JSR是针对Java语言规范的,我很想帮他们进行评审。甚至现在都不太能称作是专家组了,只是Alex Buckley和Oracle语言团队在重新审视语言规范,并修补规范里的错误。

Oracle会说:“这是这版规范已经修复掉的bug列表,还有这是修改过的规范”。于是我顺着这个列表去网站上复查这些bug,却发现它们没法公开浏览。我都不知道哪些bug是他们声称已经修复掉的,那我又能有什么办法?

我也得不到足够的信息。他们说:“啊,我们错了,它们应该对外公开,我们正在处理此事”。然后公开评审阶段就这么取消了,过了一两个星期他们说:“噢,这些bug已经可以看了”,但这已经太晚了,应该在公开评审阶段去获得公开的反馈意见。

当然,他们会很乐意看到在公开评审阶段出现内部和外部的反馈意见。公开评审阶段是个比较正式的时期,他们也乐意在任何时候接受反馈,所以这其实也只 是语言规范方面的一小部分情况而已。但是,他们对于外部环境的重视程度还是不如Sun公司以前那么好。他们更看重内部意见。他们有自己的开发人员,他们并 不像Sun公司那样乐于接受公司外部人员的贡献。

作为一种实际举措,现在他们把大多数工作都放在内部进行,以前是如此,未来可能也一样。Sun公司会很热心地鼓励社区加入整个过程,这跟Oracle很不一样。我觉得现在他们就像有两条左腿一样笨拙,他们应该还在学习吧。

InfoQ:你觉得他们的确在尝试吗?

Neal Gafter:我想是的,他们应该不是故意的。在有可能是“无能为力”的情况下,我从来不会把他们当成有什么恶意的想法。我想可能是他们如今要面对的社区和他们之前所接触的社区有太多区别的缘故吧。

他们正在学习如何与社区协作。虽然起步不尽如人意,但我想他们肯定很想做好。他们一定在不断努力成为社区良好的协作者。在Oracle内部技术方面的问题倒不多,主要还是管理的问题。

假如他们想要发布什么消息,或者想要建立一个公开的邮件列表,他们却找不到合适的管理途径。这个公司实在太大了,他们没法让人们自力更生。他们必须通过某些流程来批准或颁布某些消息,而现在他们想要解决的这些事情没法用上之前那些流程了。

所以我觉得他们是想解决问题的,给点时间吧。

目前的情况肯定没法令人满意,但我想他们的目标肯定是想成为社区的良好协作者。他们想要创建社区,因为他们从社区里得到的收获比任何人都要大。

他们的目标如此,我相信他们。前段时间在公开度方面我对他们很严厉,但我知道他们在努力。就算他们以后有了进步,我也一样会继续督促他们,因为总是会有提高空间的。

至少我认为他们的方向?没有问题。

InfoQ:我相信下一个问题已经有很多人问过你了,但我对你现在是在为微软工作这件事还是很感兴趣。显然微软和Sun的关系并不怎么样,由于各种原因现在可能已经好些了,但某人为C#工作的同时,还热情地参与Java的发展实在是很有趣的事情。你是怎么做到的呢?

Neal Gafter:嗯,其实这并不是我的工作,我是指,Java跟我在微软的工作没有任何关系。我参与 Java发展,只是因为我在乎,我感兴趣,而且我的很多朋友也在其中。你知道,我过去很长时间都投身于Java,我一直在关注它的发展。而且,从微软身上 我也学到了很多有利于Java的东西。

许多Java在成长过程中会遇到的问题,其实在C#那里已经出现过了。许多Java目前正在努力尝试的东西,其实也是C#之前想要去做的,C#这么 做了,而且做的很不错。不过C#世界也不是尽善尽美的。我的意思是,我们学到了很多东西,这对Java来说都是十分宝贵的经验。

如果要我说哪些东西是我想带给Java世界的话,举个例子吧,你们有没有看过C#正在构建的异步语言特性?还有C#里的Lambda表达式?

你要知道,像C#和LINQ所带来的并发模型操作等等,我觉得都是对Java世界很有帮助的东西。

InfoQ:我猜C#有一些优势,尤其是想要增加一些大功能的时候,C#的用户会比Java小一些,因此可以更轻易地添加一些破坏向后兼容性的功能。还是说,这只是处理问题哲学上的区别?

Neal Gafter:我想只是哲学方面的区别吧。我猜你其实主要是在说泛型。

InfoQ:是的。

Neal Gafter:所以先为读者明确一下背景吧:在Java增加泛型功能时,它们选择了擦拭法(Erasure)。

换句话说,这种做法不需要对虚拟机进行深度改变,工作都在编译期间完成了。在运行期间,所有信息,或者说大部分和泛型有关的信息已经从实际的对象上擦除了。你无法在运行时区分一个字符串列表和一个整型列表,它们在运行时的处理方式完全一致。

这么做的原因之一?,是因为我们想直接将现有的类库泛型化──并且让类库的使用者和实现者独立开来。我们不想强制他们的先后顺序,也不想让Java提供商们准备一个“泛型前”的版本和一个“泛型后”的版本。

否则,如果你基于类库开发的话,那么你必须针对每个版本都构建一份,对吧?理论上说这会十分复杂。实际上,我们对复杂性的害怕程度远胜于类库提供商。

当C#增加泛型功能时,它们并没有废弃旧有的类库。这些集合类型依然在类库里。事实上泛型容器的接口和非泛型的接口有继承关系。泛型的类型会扩展非泛型的那些。

InfoQ:明白。

Neal Gafter:所以,当你在实现一个泛型容器时,你其实也在实现非泛型的容器。这带来了一定程度上的互操作性。这意味着当你创建了一个泛型的容器之后,可以将其传递给需要旧式容器的地方。

反之则不然。假如你有一个旧式的容器,那么你就不能将其交给泛型容器使用。不过其实只要创建一个新的容器来存放所有元素就行了,不会有什么问题。

不过话说回来,我其实并不觉得“让JSR-14在工程上易于实现”是一个明确的目标。我们没有说过“想要最小化增加泛型特性的工作量”。不过 Java增加泛型的做法,比微软平台的做法要省事得太多太多了。微软平台不仅要对编译器进行大量修改,还需要深度修改虚拟机和运行时类库。我的意思是,从 系统整体来看,需要进行更多更彻底的修改。

原则上讲,这样的改变不会破坏现有的类库。不过如果你想要把现有的类库迁移至泛型版本,则需要做进一步的修改,因为它着实改变了。我并不觉得Sun如果采取了C#的做法会对用户带来多大负担,但Sun的工作量就会大一些了。

Sun会有许多事情要做,事实上他们并没有足够的资源去做这些事情。

InfoQ:没错。

Neal Gafter:至少没法在计划时间给完成。要知道,这可能会需要花费好几年的时间,还需要十几个额外的开发人员才能实现微软的做法。微软对.NET平台投入的研发资源总是比Sun的投入要大很多。而现在从功能角度来说,Sun则需要比微软投入更多研发资源了。

我认为微软一般会提供更为彻底的设计、测试以及良好集成的系统──它的审查绝对更为仔细,系统的各个部分在一起工作时都比Sun更为清晰。举个例 子,在1.1版本中,Sun为平台同时添加了内部类和序列化功能。我没有参与这部分工作,但就我的理解来看,负责这些功能的团队在工作时都是相互独立且正 交的,但实际上不应该如此。

这些不是正交的功能,这导致这两者直接之间的交互始终令人感到不舒服。我的意思是,这些功能的边界上总有无可避免的弱点。如果投入更多时间,更多资 源,更多测试和审查,虽然的确会需要更长的时间,更昂贵的开发成本,但我认为这无疑会得到更好的结果。就我的体会而言,C#在很多方面的设计比Java要 扎实许多,泛型便是其中一例。

不知道这有没有回答你的问题。

InfoQ:是的,谢谢。Java社区长久以来有一个争论,我想在.NET社区里也是如此,就是说“是否真有必要不断添加特性”。 会不会到某个时候语言变得足够复杂了,因此必须停止添加特性?很多人抛出的例子是C语言,它基本没有改进,但它始终运用广泛可能也是因为这个原因。与此相 反,C#却在不断提供快速的改进。你支持哪边?你的观点是什么呢?

Neal Gafter:我认为改进是必须的,但处理起来需要格外小心。Java在这方面会比C#要困难,有几点原因。

第一点:资源限制。

第二点:与C#等语言相比,Java缺少长期的计划。

C#的计划十分清晰──我是指Anders Hejlsberg的计划。他是整个语言和平台的架构师,他的设计感非常好。尽管他对别人干涉很少,这就是他与同事一起工作时的协作风格,但在语言该何去 何从的问题上他有着十分清晰的长期观点。每次在改进时,他首先考虑的就是:这是让语言朝着那个方向在前进,还是由于某人的偏好而跳向了另外一边。

有很多事情会让某些人感到满意,但对更多人来说却没有多大收获。如果某些东西在添加以后并与人无助,这实际上便是损害了他们的利益。尽管他们不会使用这些东西,甚至看也不会看一眼,也不会关心,但其实还是让整个系统变得复杂了。

所以,我们在微软内部考虑每个语言提案的做法,就像是从负1000分开始,在开始真正考虑将其添加到语言中之前,你还需要和这负1000分进行搏斗。之前我的感觉还只是负100分,但现在的确得看作是负1000分了。标准变的越来越严苛,对语言来说应该如此。

即便是C语言也在改进。它有标准委员会,新版本的标准出来之后的确会带来改进,长期以来也有了大量变化。不过C语言的委员会对待改进的态度,的确比像C++这样的标准委员会要保守地多。

在考虑哪些该放入语言哪些该拒绝掉的时候,C++标准委员会的态度就宽松地多了。你知道,我并不反对保守主义,但我同时觉得如果一门语言在目前还是举足轻重的话,就必须在一定程度上拥抱变化。

对Java来说,较难改变的原因之一,是很多在大道理上讲是不错的变化,但是他们的实现方式并没有考虑语言的未来发展。这对我眼中的“长期计划”来说其实是种倒退。

泛型便是最好的案例之一。在添加泛型特性时,一些人对人们移植类库的复杂度有所顾虑,因此他们使用了擦拭法进行简化。但问题是,擦拭法让人难以添加后续的语言特性。例如,在使用擦拭法的泛型系统中,为语言添加函数类型就变得困难了许多。

InfoQ:如果我没记错的话,你在一篇博客中对此进行了辩护,说还是有办法修补的。你可以实现一种不依赖擦拭法的泛型。是这样吗?我记得我看过这篇文章。

Neal Gafter:没错。事实上根据我的提议你可以同时拥有两者,但最终你还是会得到非泛型的类型,擦拭后的泛型类型参数,以及具体化的泛型类型参数。你永远无法避开擦拭法,有些类型参数始终会被擦除,还有一些则永远不会。

但问题是,如果语言的未来演化是朝擦拭的方向进行的,那么这种做法不会带来什么帮助。之前如果对语言产生了影响的,那么还是会继续产生影响。对于那 些想要使用泛型,且需要具体化类型参数以便实现某些功能的人来说,这的确会有所帮助。但擦拭法在许多方面依然会碍手碍脚。我知道也有人在探索如何可以在获 得具体化泛型的同时讲破坏降低到最小。但我们做得到吗?

这其实很难,不过也有些人有信心在这方面取得进展。如果他们成功了,那么理论上说,某次发布就会导致之前运行良好的一些泛型代码出现问题,但之后你可以获得具体化的泛型功能。如果真能做到了这点,我觉得就再好不过了,但对此我还是有所怀疑。

InfoQ:是啊,对此我也很矛盾,但的确很有趣。Stephen Colebourne声称Oracle应该推出一个不向后兼容的Java版本,修复这些走错路的问题,最好还能同时维护两个版本。这样……

Neal Gafter:好吧,这其实不就是微软做的事情嘛,只不过改名叫做C#了而已。我的意思是,我会这样问:为什么非要Oracle来搞?

为什么要叫做Java?你知道,为什么非要跟Java扯上关系?如果不向后兼容,那么就不是Java语言了。如果你希望它运行在Java虚拟机上,并提供具体化的泛型功能,这也会遇到麻烦,因为具体化的泛型功能需要虚拟机的支持。

所以,既然是个新的虚拟机,也是个新的编程语言,那么为什么要Oracle来做这件事情?

这也是我给自己的问题。我绝对相信Java以外的语言也有发展空间,否则我也不会加入微软了。即便是在Java虚拟机上也有其他一些很不错的选择,例如Scala。

要知道,如果在JVM上我可以选择语言来使用的话,Scala在我的偏好里的排名会十分靠前。

InfoQ:在新兴的语言里有哪些趋势是值得关注的呢?你提到Scala是OO和函数式编程的混合体,我想.NET里的F#也是类似的东西。你觉得在超越函数式编程之后会是什么呢?

Neal Gafter:嗯,超越函数式编程?我想会有很多方面──我这么回答多少有些回避问题吧。

但我的确是这么想的,尤其是像Java和C#这种一开始是从命令式编程发展起来的语言,从函数式编程社区那里还有许多点子可以借鉴过来,这种挖掘探索还会持续几年。

不过,我不觉得Java或C#能够走的像你期望中那么远,它们永远不会成为函数式那样的编程语言。我的意思是,它们的本质依旧会是命令式的,但你可 以体验到许多函数式编程方式所带来的快感,尤其是使用不可变数据进行编程的时候。我想,如果你想真正获得并发编程的优势,但还是在使用传统的锁和信号量 ──就是那些Java很早就一直提供的东西──那么基本就无法进行良好的扩展。

你没法用这种方式开发大规模的并发系统。好吧,可能你做得到,但我不行。在进行大量的数据操作以及Fork-Join并发编程时,不可变的数据是至关重要的,共享的可变数据往往会是问题的根源。

解决方案便是不要共享或不要修改。

而函数式风格意味着你不会进行修改。你不修改状态,只会使用不可变的数据。在Java语言里支持这种风格还需要一些努力,类库也一样,尤其是基础支持类库更有许多事情要做。如果我们希望这种编程风格被人广泛使用,则还有很长的路要走。

InfoQ:所以你的看法是,如果我们要提高编程效率,必须提供明显的多核支持或是类似的东西?

Neal Gafter:是的,我认为这会以“润物细无声”的方式进行改变。

我认为,在实际操作的时候,我们会找到性能和稳定性问题所在,然后使用更易扩展的方式来逐一替代每个子系统。

InfoQ:一些现有的做法,例如Actor模型或是Erlang那样的消息传递方式,你觉得它们对于Java或C#来说是否合适呢?

Neal Gafter:有这个可能,不过这需要语言方面的一些改变,没人计划朝这个方向去走。Actor风格的关键之一是模式匹配,Scala用case类来进行模式匹配。

目前还没人打算在Java里实现这个。我想这个特性还是值得考虑的,但我还是认为这需要一个高屋建瓴的长期计划,这样我可以清晰地看到这将成为Java的一部分。

但你不能缺少一个长期计划就开始做事,不能因为只是有人在那里说“啊哈,在版本X里我们要加入Actor模型”,一定需要一个长期计划。目前在 Java里创建一个表示不可变数据的类型还很尴尬,应该提供一些更简单的办法。此外也应该能够轻松地进行模式匹配。我觉得目前的序列化功能在处理不可变数 据时还不够理想,但可能也不是问题?即便Actor模型与目前还没影的不可变数据类型会是绝配,但目前的Java还没有准备好,它们搭配起来会有问题。

我估计未来一定会有其一席之地,因为它的价值无庸置疑,但我还没有从Oracle那里听说这方面的消息,所以肯定不会是Java 7或8。如果它真会出现,那也是在这些时间计划以外了吧。

不过我认为,如果你希望用那种形式编程──这的确会带来许多优势──那么你(至少现在)应该使用另一种更自然的语言。对Java和C#这种语言来说,想要自然的表达Actor模型还需要太多改变。所以使用Scala吧,或者.NET平台上的F#。

InfoQ:我猜.NET平台和Java平台的一大趋势便是支持C#和Java外的其他语言,只不过微软从一开始就强调了而已。

Neal Gafter:当然,你看到Java里面有java.lang.Object,对吧?

InfoQ:是啊。

Neal Gafter:它应该是面向所有语言的的对象,只不过当初在一开始的时候大家都还缺少这方面概念而已。

InfoQ:喔。John Rose在领导一个Java项目,这也是Da Vinci项目的一部分,它支持像尾递归这样的东西。如果你有机会做一些面向未来的项目,你会做什么呢?它会是什么样的?

Neal Gafter:这个大方向其实是想要尝试去做好几种不同的东西。例如支持动态语言,目标之一便是希望 让这个虚拟机更容易实现动态语言,这也是Da Vinci项目最主要关注的东西。你想,我们可以加入哪些东西来支持不同的编程语言?还有便是如何让平台上不同语言之间的互操作变得更为简单。这也是微软 的目标,它提供了DLR以及C#语言里的dynamic关键字,希望可以增加语言之间的互操作性。

微软做的那些事情,是希望让那些语言在提高执行效率的同时,还支持高效的动态分派,还提供了一种元对象(meta-object)协议。这样,某种动态语言里的对象,也能被另一种编程语言通过一种合适的语义进行访问,这样语言之间便可以自由互通了。

我的想法是,无论是在虚拟机层面还是虚拟机之上增加这样一种元对象协议,对于动态语言之间的互操作都是很有好处的。

InfoQ:后者就是.NET的做法,是吗?它是CLR上的一个类库。

Neal Gafter:是的。嗯,事实上CLR还是提供了一些支持的,不过基本上可是认为是CLR上的类库。

还有一个方面便是Java编程语言上的支持。在C#里增加了一个“dynamic”类型,它的意义是:“在运行时探究其语义。我们在编译器指定要做哪些事情,但在运行时确定其语义”,这便让C#能够使用动态语言创建的对象了。

所以我认为提供一个动态的元对象协议是个十分有用的方向。

在支持动态语言方面,我也有很多东西是乐于在虚拟机里见到的,尾递归便是其中一项。这不是个大功能,但对于某些编程语言来说至关重要。

还有便是分段式栈(segmented stack)。目前在Java和C#里都有这样一个问题,这些平台避免了很多C或C++这类原生语言容易出现的稳定性问题,例如越界错误,因为每次访问数 组下标时都会进行范围检查。你也不能访问已经释放的内存,或是还未分配的内存,因为你不能直接操作内存空间,这是垃圾收集器做的事情。但是这些平台目前还 没有合适的处理栈溢出的方法。栈溢出并不代表一定是出错了,可能只是出现了预期以外的无限递归而已。

栈溢出是基本是因为递归造成的。我是做编译器的,如果你想要让Java编译器崩溃,只要简单写一句“int i = 1 + 1 + 1 + 1”重复几千次就行了。语法分析器或解析器会去分析这句代码,然后栈就爆了,其结果便是进程崩溃了。

要知道出现这个问题后很难补救?。可能你会说:“要不就重头开始吧,我们来分配更大的栈空间”。但问题是,你很难判断该分配给这个线程多大的栈空间。

像Google的Go系统里提供的分段式栈在这方面的优势在于,你无需提前决定栈空间的大小。它会根据程序的需求动态调整,这让很多程序写起来就容易多了。

Java的线程十分昂贵,所以会有线程池。如果不贵就没必要将它们池化了。它们昂贵的原因是占空间需要提前分配好,这些栈空间都太大了,会占用太多资源。你肯定不想因为分配很大的栈空间导致所有资源用尽,而有了分段式栈以后,线程之类的东西就轻多了。

线程的代价降低以后,很多东西就应运而生了。例如异步,很多人看到异步编程就想到状态机或co-routine。如果线程足够轻便,那么这些就都不需要了。你只要创建一个线程,然后让它等待着,因为在等待时并不会占用任何资源。

这样在虚拟地址空间里甚至不会有栈分配这回事情了,所以我觉得在Java里考虑一下这方面问题是很有价值的。

这会给平台带来细微,但我相信也是十分重要的变化,也很有好处。这会影响你思考这个平台上哪些东西是需要的或是不需要的。所以要我说的话,我会把票投给分段式栈,或者是例如虚拟机级别的co-routine的东西。

InfoQ:非常感谢您接受采访。

受访者简介

Neal GafterNeal Gafter是Java SE 4和5语言增强的主要设计者和实现者,他的Java闭包实现赢得了OpenJDK创新者挑战赛的大奖。他也在继续参与SE 7和8的语言发展。之前Neal在为Google的在线日历工作,也曾经是C++标准委员会的一员,并曾在Sun微系统公司,MicroTec研究院和德 州仪器领导开发C和C++编译器。如今Neal在微软开发.NET平台编程语言。Neal是《Java Puzzlers:Traps, Pitfalls and Corner Cases》(Addison Wesley,2005)一书的合作者。他拥有罗彻斯特大学大学计算机科学的博士学位。

 

查看原文:http://w?ww.infoq.com/articles/neal-gafter-on-java


给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。

This entry was posted in Achitecture. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s