之前出了一个视频,介绍 JDK 23 中的新特性。之后我才发现,在 JDK 21 和 22 中的预览功能“字符串模板(String Templates)”,在 JDK 23 中已经没有了。字符串模板的相关代码,已经被全部删除了。
字符串模板的功能,Java 社区已经期待太久了。其他主流的编程语言,都提供了字符串插值的功能。Java 语言一直没有,也确实说不过去。JDK 21 中字符串模板的出现,解决了这一长期问题。JDK 23 直接把字符串模板删除,确实有点突然。
至于为什么要删掉字符串模板,这个功能的开发者给出的理由是:字符串模板的当前设计存在一些问题,但是具体要怎么改,又缺乏足够的共识,所以就直接先删掉了。等再收集一些反馈,达成了足够的共识之后,再把字符串模板加回来。
关于字符串模板设计上的问题,Brian Goetz 写了一段很长的文字来说明,感兴趣的人可以看原文。
总的来说,现在的字符串模板的处理器的设计过于复杂,完全没有必要。说到这里,我们再回顾一下字符串模板的设计,具体的内容可以看我之前的视频。这里简单地说一下。
字符串模板用 StringTemplate 来表示,一个字符串模板中可以包含表达式,比如,My name is \{name} 是一个字符串模板,其中 name 是表达式。StringTemplate.Processor 接口,把 StringTemplate 对象转换成其他任意类型对象。内置的 Processor 实现 STR,把 StringTemplate 转换成 String,也就是进行字符串插值。
Processor 设计的出发点是允许创建自定义的转换器,把 StringTemplate 安全地转换成其他类型的对象。举例来说,可以创建一个名为 DB 的处理器,把 StringTemplate 转换成 JDBC 中的 PreparedStatement。但是转换成 PreparedStatement 并不是终点,实际的目的是执行 PreparedStatement,获取数据库查询的结果。DB 处理器的一般使用方式类似下面这样。这段代码会被添加在数据库查询类的某个方法中。
var stmt = DB."";
stmt.executeQuery();
对于这样的使用方式,完全可以去掉 Processor 的实现,把 Processor 的实现代码直接添加到当前类中。当前方法改为接受一个 StringTemplate 类型的参数。修改之后的代码如下所示。
ResultSet query(StringTemplate template) {var stmt = ; // DB 处理器的代码实现return stmt.executeQuery();
}
新的实现完全去掉了 Processor 的实现类,更容易理解。当前的数据库查询类,很可能已经有一个 String 类型的参数来表示 SQL 语句,再添加一个 StringTemplate 类型的参数,从接口上来说很容易理解和使用。
ResultSet query(String sql)
ResultSet query(StringTemplate template)
另外一个例子是解析 JSON 字符串的 JSON 处理器,与其使用 StringTemplate 的Processor,还不如在解析 JSON 的类中,添加一个新的 parse 方法,参数是 StringTemplate。这样在实现上更紧凑。
<T> T parse(StringTemplate template, Class<T> type)
总的来说,虽然 Processor 接口设计的出发点是好的,但是在实际使用中却是多余的。
这里分成两种情况,第一种情况是 Processor 产生的是最终的结果,需要被外部消费。这一类场景通常是把某种类型格式的字符串表示,解析成对象形式。如 JSON 和 XML 的解析。这一类的 Processor 实现,看似有一定的通用性,实际上,只需要在已有的 JSON 或 XML 的解析类上,添加一个以 StringTemplate 作为参数的方法即可。这样更符合解析库使用者的使用方式。
第二种情况是 Processor 产生的是中间结果,还需要后续的处理。这一类场景通常是使用字符串作为输入,解析之后再进一步处理。典型的例子是上面介绍的数据库查询。Processor 的处理结果,并不会单独拿出来使用。对于这一类情况,同样可以添加一个以 StringTemplate 为参数的方法。使用 Processor 来实现实属多此一举,Processor 实现被复用的可能性也比较低。
综上所述,把 StringTemplate 单独拿出来使用就足够了,Processor 接口实际上是多余的。
字符串模板因为设计上的问题,短时间内无法达成一致,被从 JDK 23 中删除了。删除的主要原因,是为了避免有更多的代码使用这一有瑕疵的设计,导致以后修改起来更困难。
JDK 中的预览功能,其出发点是为了对新的想法进行试验,并获取反馈。所以预览功能被删除,从流程上来说是合理的。但是负面的影响仍然是很大的。
一个预览功能,从想法的产生,到设计,再到实现,并最终出现在 JDK 中,这是一个很漫长的过程,其中所花费的成本很高。预览功能一旦进入到 JDK,就会有人去使用它。 一个预览功能被删除了,使用它的代码都需要被修改。虽然使用预览功能是风险自负,但是在 JDK 使用者的印象中,预览功能的稳定性是足够的。在过往的历史中,预览功能可能会有小的修改,但是总体的设计和实现是稳定的。提前使用预览功能的风险其实很低。字符串模板这个预览功能被删除,打破了 JDK 用户对于预览功能的稳定性的预期。在这之后,大家再使用 JDK 的预览功能,积极性就会低很多,因为不确定性变高了。从结果上来说,不利于 JDK 的预览功能获取到足够的反馈。
以上就是 JDK 23 中,字符串模板被删除所相关的内容。只能说,确实很意外。不过既然已经是既成事实,用了字符串模板的应用,需要考虑今后的兼容性问题。因为最早引入字符串模板的 Java 21 是 LTS 版本,应用预计会使用这个版本运行较长时间,因此可以考虑保留已有的使用字符串模板的代码。下一个 Java 的 LTS 版本是 Java 25,不确定字符串模板是否会在 Java 25 回归。
点击【阅读原文】查看OpenJDK邮件组中关于字符串模板被删除的原始说明。