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:
- String indent(int n): Adjusts the indentation of each line of this string based on the value of n, and normalizes line termination characters.
- <R> R transform(Function<? super String,? extends R> f): This method allows the application of a function to this string.
And following methods related to JVM Constants API (JEP 334):
- Optional<String> describeConstable(): Returns an Optional containing the nominal descriptor for this instance, which is the instance itself.
- String resolveConstantDesc(MethodHandles.Lookup lookup): Resolves this instance as a ConstantDesc, the result of which is the instance itself.
String::indent()
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:
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:
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.
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.
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:
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;
}