Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8350031

(spec str) StringBuffer and StringBuilder methods improperly require "new" String to be returned

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • 25
    • core-libs
    • None
    • behavioral
    • minimal
    • Hide
      The relaxed spec allows implementations to return a new String instance or an existing one, but whose value must be as expected. Most programs depend only on String values. There is always the possibility, though unlikely, that some program out there depends on the identity of the String object returned. Given that the noncompliant behavior slipped into a couple LTS releases and nobody noticed any functional issues -- as opposed to performance issues (see the Problem section) -- the compatibility risk seems minimal.
      Show
      The relaxed spec allows implementations to return a new String instance or an existing one, but whose value must be as expected. Most programs depend only on String values. There is always the possibility, though unlikely, that some program out there depends on the identity of the String object returned. Given that the noncompliant behavior slipped into a couple LTS releases and nobody noticed any functional issues -- as opposed to performance issues (see the Problem section) -- the compatibility risk seems minimal.
    • Java API
    • SE

      Summary

      Modify the specifications of StringBuilder and StringBuffer to be less restrictive about exactly when they are required to create new String instances.

      Problem

      Several methods that return String and CharSequence have an assertion of the form "returns a new String that...." This is unnecessarily restrictive, as there are cases where an existing String instance that has the right value might already be available. The most common case of this is the empty string literal (""), an instance of which is typically interned, which a method might want to return if it's asked to return a zero-length String. (But there might be other cases too.)

      The "returns a new String" wording has been consistent in the specifications of StringBuffer (and later StringBuilder) since its introduction around JDK 1.2. A quick survey of historic JDKs revealed that they conformed to this requirement, as an expression like new StringBuilder().substring(0) returned a new, empty String instance distinct from the interned empty String. In JDK 15 and later, however, the actual interned empty String was returned in such cases, which strictly speaking was noncompliant. This seemed also to be true in JDK 17 and JDK 21, but the implementation changed a couple times as fixes were developed and backported. (See JDK-8332282 for more history.) In that time no applications seemed to have been affected, although benchmarks have detected the changes.

      Solution

      Adjust the specification so that some String is returned that has the expected value, but not necessarily a "new" String.

      Specification

      Changes are made to the specifications of the following methods in the AbstractStringBuilder class. This is a (private) superclass of StringBuilder and StringBuffer, both of which inherit these methods and their specifications, so identical changes will appear in the public specifications in StringBuilder and StringBuffer.

           /**
      -     * Returns a new {@code String} that contains a subsequence of
      +     * Returns a {@code String} that contains a subsequence of
            * characters currently contained in this character sequence. The
            * substring begins at the specified index and extends to the end of
            * this sequence.
            *
            * @param      start    The beginning index, inclusive.
      -     * @return     The new string.
      +     * @return     A string containing the specified subsequence of characters.
            * @throws     StringIndexOutOfBoundsException  if {@code start} is
            *             less than zero, or greater than the length of this object.
            */
           public String substring(int start) { ... }
      
           /**
      -     * Returns a new character sequence that is a subsequence of this sequence.
      +     * Returns a character sequence that is a subsequence of this sequence.
            *
            * <p> An invocation of this method of the form
            *
            * <pre>{@code
            * sb.subSequence(begin, end)}</pre>
            *
            * behaves in exactly the same way as the invocation
            *
            * <pre>{@code
            * sb.substring(begin, end)}</pre>
            ...
            */
           @Override
           public CharSequence subSequence(int start, int end) { ... }
      
           /**
      -     * Returns a new {@code String} that contains a subsequence of
      +     * Returns a {@code String} that contains a subsequence of
            * characters currently contained in this sequence. The
            * substring begins at the specified {@code start} and
            * extends to the character at index {@code end - 1}.
            *
            * @param      start    The beginning index, inclusive.
            * @param      end      The ending index, exclusive.
      -     * @return     The new string.
      +     * @return     A string containing the specified subsequence of characters.
            * @throws     StringIndexOutOfBoundsException  if {@code start}
            *             or {@code end} are negative or greater than
            *             {@code length()}, or {@code start} is
            *             greater than {@code end}.
            */
           public String substring(int start, int end) { ... }
      
           /**
            * Returns a string representing the data in this sequence.
      -     * A new {@code String} object is allocated and initialized to
      -     * contain the character sequence currently represented by this
      -     * object. This {@code String} is then returned. Subsequent
      +     * The {@code String} object that is returned contains the character
      +     * sequence currently represented by this object. Subsequent
            * changes to this sequence do not affect the contents of the
      -     * {@code String}.
      +     * returned {@code String}.
            *
            * @return  a string representation of this sequence of characters.
            */
           @Override
           public abstract String toString();

            smarks Stuart Marks
            smarks Stuart Marks
            Alan Bateman, Raffaello Giulietti, Roger Riggs
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: