Saturday, December 22, 2018

The Brief but Complicated History of JDK 12's String::transform Method

It was recently proposed that the Java preview feature Raw String Literals (JEP 326) be removed from JDK 12 and it is now official that the preview feature will be removed (version 25 of Java SE 12 [JSR 386] removes it). Several methods had been added to the JDK String class to support this feature. Those methods that were added to versions of the JDK prior to JDK 12 [such as String::lines] are likely to remain available even after the raw string literals preview feature has been removed. However, it has already been decided that one method added to String in JDK 12 (String::align) should be removed from JDK 12 as part of removing raw string literals. The method String::transform was added to JDK 12 and the remainder of this post looks at String::transform, as currently implemented in JDK 12, in more detail and discusses why its already controversial short history implies that it could be a potential candidate for removal along with raw string literals.

The current String::transform implementation has been available in JDK 12 Early Access Builds since Build 22 (Build 24 [15 December 2018] is latest available build as of this writing) and was introduced via JDK-8203442 ("String::transform").

There was quite a bit of discussion related to this method being added to the JDK. The following bullets outline key discussion points.

  • Jim Laskey wrote that the "originating goal" of String::transform was to "allow custom alignment methods for those developers not satisfied with String::align()"
  • Other messages further describe the motivation for, intent of, and benefits of String::transform:
    • Rémi Forax wrote, "...it's nice to be able to be able to write code fluently from left to right..."
    • Jim Laskey wrote, "String::transform was intended to facilitate custom manipulation (alignment) of raw string literals, in the most string generalized way."
    • The "Description" of JDK-8203442 states, "The String::transform instance method allows the application of a lambda function to a string."
    • JDK-8203703 supplies examples to illustrate that "...steps can be discerned more clearly" with String::transform than with static methods in which the "reader is forced to interpret portions of the expression from the inside out."
  • String::transform originally returned String, but then was changed to return Object and Jim Laskey wrote about that change, "'transform' became generic when the case was made that other types might also be relevant." He concluded, "I might be led back to just supporting String."
  • The naming of String::transform has been challenging with some of the following names proposed (listed in alphabetic order):
  • Rémi Forax has written that "more variants (transformToInt, transformToLong, transformToDouble) [are needed] to be useful."
  • Brian Goetz has described why the current plan is to implement this functionality via the method String::transform rather than an operator such as |>.
  • Stuart Marks has written that "this particular decision [String::transform] sets a precedent for the use of the name 'transform' for methods that do similar things on other classes" and references JDK-8140283 and JDK-8214753:
    • JDK-8140283 proposes the addition of the "chain" method for Stream and Optional to "mitigate" the "disrupt[ion of] the linear flow of the pipeline stages" when using methods that acts upon a Stream or Optional and return something that is itself "chainable").
    • JDK-8214753 proposes the addition of "Optional::transform" that would allow for "an arbitrary operation on an Optional."
  • There was some confusion and consternation related to how String::transform was added to OpenJDK 12, but Stuart Marks's message summarizes the events leading to the addition of this method.
    • A particularly interesting sentence in Marks's message states (I have added the emphasis): "While this API point stands on its own, this is really part of Jim's RSL work which includes several API additions to String, and which will likely have a significant effect on how String literals are used in Java code."
  • Tomasz Linkowski has pointed out that it's likely that String::transform (and any similar method added to Stream) will get used in select cases where there are easier ways to do the same thing already without the new method. The examples he provides of potential misuse of String::transform are "string.transform(String::toLowerCase)" and "stream.chain(s->s.map(mapper))".

Two online examples demonstrate how String::transform might be used in its most common use cases:

  • JDK-8203703 ("String::transform") provides a "Solution" example that demonstrates how String::transform can improve code readability by allowing for operations acting on Strings to be read in order from left-to-right rather than being read "from the inside out."
  • A message on the core-libs-dev mailing list provides an example of using String::transform to convert a String into an instance of a class other than String.

Stephen Colebourne asked the same question I was wondering when I read that raw string literals were to be removed from JDK 12: "Is String::transform going to be removed as well given the removal of raw strings and its controversial nature?" Although I have not seen anything authoritative and definitive regarding whether String::transform will remain in JDK 12, there are three pieces of evidence that lead me to think it will be staying.

  1. I have not seen anything saying that String::transform, which is already in JDK 12 as of Early Access Build 22, is to be removed. There are issues written to remove compiler support associated with raw string literals and even to remove another String method (String::align), but I'm not aware of a similar issue written for String::transform.
  2. It has been stated that while String::transform was added as part of the raw string literal work, it was also stated that String::transform "stands on its own."
  3. The two examples I cited earlier on how to use this method do not rely on or require raw string literals. In other words, the method can be used regardless of the presence or absence of raw string literals.

String::transform has not been around for a long time (less than one year), but it already has significant history. The method is available currently in JDK 12 (since Early Access Build 22) and I suspect it will remain part of String's API despite the removal of raw string literals from JDK 12.

1 comment:

@DustinMarx said...

JDK 12 Early Access Build 28 includes a bug fix related to String::transform (JDK-8215112), but it's only an addition to that method's Javadoc comment to add that "Any exception thrown by {@code f()} will be propagated to the caller." The full list of changes for JDK 12 Early Access Build 28 is available and shows fixes for related issues "8215489: Remove String::align" and "8215493: String::indent inconsistency with blank lines".