技术开发 频道

语言不变下 如何助推Java的不断演进

  Java 6注解处理器

  相对于Ruby、Smalltalk和Lisp来说,DSL在很多主流语言(如Java)中的流行程度就稍逊一筹了,但最近Java语言的一些变化(尤其 是Java 6中新增的注解处理器)为开发者提供了新的机遇以在其中使用DSL。对于Java EE 6中的JPA 2.0来说,其某些API本身就是 DSL。注解处理器会为应用中的每个持久化类建立一个元模型类型(metamodel type)。虽然开发者可以手工处理Java中的元模型,但这实在太无聊而且极易出错。注解处理器的出现改变了这一切,因为它内建于Java 6,因此无需特殊的IDE支持——IDE会代理编译器所触发的注解处理器,之后会自动生成元数据模型。

  程序库也可以通过注解处理器来提供新的语言特性。比如说,Bruce Chapman的原型“no closures”提案就凭借该技术将方法转换为 Single Abstract Method(SAM)类型,然后在Java 6上编译。在与其交谈过程中,Chapman指出SAM类型还支持自由变量(free variable),这是闭包的一个关键技术,除了Single Abstract Method所需的参数外,方法体还可以通过@As.Additional注解声明额外的参数。在获得SAM类型的实例时,这些参数可以带有绑定值,然后在每次调用时传递给方法。

  Chapman还创建了Rapt项目以探索该技术的其他使用场景,同时为语言的两个变化提供了自己的实现——多行字符串(Multiline Strings)与XML字面值(XML literals)——这两个特性是 为JDK 7准备的,但却不会包含到最终的发布中。Java甚至也可以使用这种方法实现闭包,Chapman对此说到,“我们刚刚使用该技术完成了一个Swing项目,在这个过程中发现了泛型的一些小bug,其中一个bug还没有修复,除此之外一切都很棒,没人再想使用传统 的匿名内部类了。”

  另一个探究注解处理器的项目是Lombok,它将该技术又向前推进了一大步。Lombok将注解作为回调以运行Java agent,后者会根据注解重写各种javac内核。由于操纵的是内部类,因此它不太适合于产品使用(JVM各个小版本中的内部类也可能不一样),但该项目对于注解处理器到底能做什么这个问题上还是颇具启发意义的,包括:通过@Getter和@Setter注解定义各种访问级别的属性,如@Setter(AccessLevel.PROTECTED) private String name:

  @EqualsAndHashCode注解会根据对象中的属性实现hashCode()和 equals()方法;

  @ToString注解会实现toString()方法;

  @data方法相当于@ToString、@EqualsAndHashCode、所有属性的@Getter以及所有非final属性的@Setter的集合,可以使用 @data方法和构造方法初始化final属性。

  还可以通过这种方式进行其他的语言试验,比如移除Java中的非运行时异常等。虽然注解处理器技术为语言试验开辟了一条新航线,但还是要注意生成代码的可读性,保证开发者能读懂生成的代码。Chapman给出了很多建议:

  要生成源代码而不是字节码,注意生成代码的格式(尤其是缩进)。编译器不在乎生成的代码是不是都在同一行,但用户在乎。我甚至还使用注解处理器在恰当的地方增加了一些注释和javadoc。如果这项技术逐渐流行起来,用户将可以通过IDE查看编译期所生成的代码。

  IDE中的语法糖

  Bruce Chapman还提到了第三项技术——将语法糖从语言迁移到IDE中——他在博客中对该问题做过深入阐述。对于Java IDE来说,生成部分样板代码已成为不可或缺的功能了,比如类的getters和setters,但IDE开发者刚刚开始深挖该这个概念。 JetBrains的IntelliJ 9为内部类提供了一个类似于闭包的简洁的代码块语法,开 发者也可以自己加。

  就像代码折叠一样,该语法会扩展到编译器能够处理的完整的匿名内部类——这样坚持使用标准的匿名内部类语法的开发者很容易就适应了。 Eclipse也有一个类似的插件。关键在于这种语法仅仅是实际代码的另一种展示方式而已,编译器和任何源代码管理工具都能够像以前那样处 理他们。开发者可以在两种方式间自由切换(就像代码的展开与收起一样),无权访问该语法糖定义的人仅仅会看到正常的Java代码。Chapman说到:

  要想实现易于使用的目标还有很多工作要做,但从长远来看,我发现开发者会很轻松地实现加糖/脱糖(sugaring/desugaring)的转换(可以 参考jackpot来 了解实现原理)、不断尝试、不断演进并与同事和社区分享好的点子。这么做的好处与语言的演进别无二致。最好的东西会流行起来并形成实际语言演进的基础,如 果必要的话还可以随时去除该方法无法实现的“噪音”。

  由于语法糖还要兼顾(非常多)其他的语言特性,因此无法提供完整的闭包支持;比如说BGGA闭包就有一些特性无法匹配匿名内部类,因此不能通过这种方式实现。话虽如此,这种想 法却展示了通过各种新语法来表示匿名内部类的可行性,类似于BGGA语法或是FCM语法,开发者也可以选择自己喜欢的语法。其他的语言特性(如null-safe Elvis operator)可以通过这种方式实现。要想进一步验证该想法,可以体验一下这个NetBeans module(由Chapman开发),这正是他所说的用于 Properties的原型。

  结论

  在语言的发展过程中总是需要考虑稳定与发展之间的平衡。上面介绍的技术所带来的好处是他们不会影响平台或是语言本身。这么做允许我们犯错误,也有益于进行快速激进的试验。由于开发者能够自由地进行试验,我们看到越来越多的人开始解决常见的样板代码“噪音”问题,如匿名内部类语法等,同时将这些想法以合理的方式组织起来以获得最大的价值。我们将欣喜地看到开发者使用这些方法将Java平台推向新的远方。

0
相关文章