Java 12 - New Methods in String

Although JEP 326 - Raw String Literals is dropped from JDK 12 release, Java 12 still bring enhancements to java.lang.String. Here various new methods added to String from Java 12:

And following methods related to JVM Constants API (JEP 334):

String::indent()

StringIndent.java
public class StringIndent {

    public static void main(String[] args) {
        String s = "Life is too short to work so hard.";
        System.out.println(s);
        System.out.println("string length: " + s.length());
        
        String sindent = s.indent(5);
        System.out.println("\nIndented:");
        System.out.println(sindent);
        System.out.println("string length: " + sindent.length());
    }
}
                    

Life is too short to work so hard. string length: 34 Indented: Life is too short to work so hard. string length: 40

Before inserting spaces, the input string is separated into lines. If n > 0, this method appends 'n' number of space characters (U+00200) in front of each line, then suffixed with a line feed "\n" (U+000A). The resulting lines are then concatenated and returned. Let's check following example:

StringIndentMultiline.java
public class StringIndentMultiline {

    public static int countChar(String s, char c) {
        int cnt = 0;

        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == c) {
                cnt++;
            }
        }
        return cnt;
    }

    public static void main(String[] args) {
        String s = "Life is short,\nso I'm knowing exactly where I'm putting my time.\nI don't want to do things that I don't have to do.";
        System.out.println(s);
        System.out.println("string length: " + s.length());
        System.out.println("\\n count: " + countChar(s, '\n'));

        String sindent = s.indent(5);
        System.out.println("\nIndented:");
        System.out.println(sindent);
        System.out.println("string length: " + sindent.length());
        System.out.println("\\n count: " + countChar(sindent, '\n'));
    }
}
                    

Life is short, so I'm knowing exactly where I'm putting my time. I don't want to do things that I don't have to do. string length: 115 \n count: 2 Indented: Life is short, so I'm knowing exactly where I'm putting my time. I don't want to do things that I don't have to do. string length: 131 \n count: 3

original length = 115

+ (3 * 5 space) = 115 + 15 = 130

+ 1 additional line terminator \n = 130 + 1 = 131

It's a match!

If n< 0, this methods removes 'n' numbers of whitespaces, or removes all whitespaces if number of whitespaces < n:

StringIndentMultilineNegative.java
public class StringIndentMultilineNegative {

    public static int countChar(String s, char c) {
        int cnt = 0;

        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == c) {
                cnt++;
            }
        }
        return cnt;
    }

    public static void main(String[] args) {
        String s = "Life is short,\n        but it's long enough\n to ruin any man who wants to be ruined.\n";
        System.out.println(s);
        System.out.println("string length: " + s.length());
        System.out.println("' ' count: " + countChar(s, ' '));

        String sindent = s.indent(-5);
        System.out.println("\nIndented:");
        System.out.println(sindent);
        System.out.println("string length: " + sindent.length());
        System.out.println("' ' count: " + countChar(sindent, ' '));
    }
}
                    

Life is short, but it's long enough to ruin any man who wants to be ruined. string length: 85 ' ' count: 22 Indented: Life is short, but it's long enough to ruin any man who wants to be ruined. string length: 79 ' ' count: 16

count of ' ' included whitespaces in between words in the lines. Interestingly no line terminator '\n' added if original String ended with '\n'.

If n = 0 then the line remains unchanged, but line terminator '\n' is still added at the end of the String.

StringIndentMultilineZero.java
public class StringIndentMultilineZero {

    public static int countChar(String s, char c) {
        int cnt = 0;

        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == c) {
                cnt++;
            }
        }
        return cnt;
    }

    public static void main(String[] args) {
        String s = "   The art is long,\n life is short.";
        System.out.println(s);
        System.out.println("string length: " + s.length());

        String sindent = s.indent(0);
        System.out.println("\nIndented:");
        System.out.println(sindent);
        System.out.println("string length: " + sindent.length());
    }
}
                    

The art is long, life is short. string length: 35 Indented: The art is long, life is short. string length: 36

String::transform(Function)

This method feed the provided function with a particular String instance as input and returns output returned by that function.

StringTransform.java
public class StringTransform {

    public static void main(String[] args) {
        String str = "Life's too short";
        var result = str.transform(input -> input.concat(" to eat bad food"))
            .transform(String::toUpperCase)
            .transform(String::toCharArray);
        System.out.println(Arrays.toString(result));
    }
}
                    

[L, I, F, E, ', S, , T, O, O, , S, H, O, R, T, , T, O, , E, A, T, , B, A, D, , F, O, O, D]

As you can see, we even can chain transform methods if we really feel like we need to. But I prefer to keep it as simple as possible.

String::describeConstable()

New since Java 12, String now implements interface Constable. A constable type is one whose values are constants that can be represented in the constant pool of a Java classfile as described in JVMS 4.4, and whose instances can describe themselves nominally as a ConstantDesc.

import java.util.Optional;

public class StringDescribeConstable {

    public static void main(String[] args) {
        String str = "Life is short, and we should respect every moment of it.";
        Optional<String> optStr = str.describeConstable();
        optStr.ifPresent(value -> {
            System.out.println("Value: " + optStr.get());
        });
    }
}
                    

Value: Life is short, and we should respect every moment of it.

Method describeConstable() returns an Optional containing the nominal descriptor for this instance, which is the instance itself. In short: returning an Optional of itself.

/**
 * Returns an {@link Optional} containing the nominal descriptor for this
 * instance, which is the instance itself.
 *
 * @return an {@link Optional} describing the {@linkplain String} instance
 * @since 12
 */
@Override
public Optional<String> describeConstable() {
    return Optional.of(this);
}
                    

More about Optional can be found in Java Optional Guides with Examples.

String::resolveConstantDesc(MethodHandles$Lookup)

Last but not least, method resolveConstantDesc. What you think about the code below:

StringResolveConstantDesc.java
public class StringResolveConstantDesc {

    public static void main(String[] args) {
        String s1 = "Life is short, and it is here to be lived.";
        String s2 = s1.resolveConstantDesc(null);
        System.out.println(s2.equals(s1));
        System.out.println(s2 == s1);
    }
}
                    

And the output:

true true

Also new since Java 12, String now implements interface ConstantDesc. From this interface, comes method resolveConstantDesc(...) which marks a loadable constant value, as defined in JVMS 4.4 The Constant Pool. And again, in String’s case that means returning itself:

/**
 * Resolves this instance as a {@link ConstantDesc}, the result of which is
 * the instance itself.
 *
 * @param lookup ignored
 * @return the {@linkplain String} instance
 * @since 12
 */
@Override
public String resolveConstantDesc(MethodHandles.Lookup lookup) {
    return this;
}