Coming from Perl, I sure am missing the "here-document" means of creating a multi-line string in source code:
$string = <<"EOF" # create a three-line string
text
text
text
EOF
In Java, I have to have cumbersome quotes and plus signs on every line as I concatenate my multiline string from scratch.
What are some better alternatives? Define my string in a properties file?
Edit: Two answers say StringBuilder.append() is preferable to the plus notation. Could anyone elaborate as to why they think so? It doesn't look more preferable to me at all. I'm looking for a way around the fact that multiline strings are not a first-class language construct, which means I definitely don't want to replace a first-class language construct (string concatenation with plus) with method calls.
Edit: To clarify my question further, I'm not concerned about performance at all. I'm concerned about maintainability and design issues.
43 Answers
NOTE: This answer applies to Java 14 and older.
Text blocks (multiline literals) were introduced in Java 15. See this answer for details.
It sounds like you want to do a multiline literal, which does not exist in Java.
Your best alternative is going to be strings that are just +'d together. Some other options people have mentioned (StringBuilder, String.format, String.join) would only be preferable if you started with an array of strings.
Consider this:
String s = "It was the best of times, it was the worst of times,\n"
+ "it was the age of wisdom, it was the age of foolishness,\n"
+ "it was the epoch of belief, it was the epoch of incredulity,\n"
+ "it was the season of Light, it was the season of Darkness,\n"
+ "it was the spring of hope, it was the winter of despair,\n"
+ "we had everything before us, we had nothing before us";
Versus StringBuilder:
String s = new StringBuilder()
.append("It was the best of times, it was the worst of times,\n")
.append("it was the age of wisdom, it was the age of foolishness,\n")
.append("it was the epoch of belief, it was the epoch of incredulity,\n")
.append("it was the season of Light, it was the season of Darkness,\n")
.append("it was the spring of hope, it was the winter of despair,\n")
.append("we had everything before us, we had nothing before us")
.toString();
Versus String.format():
String s = String.format("%s\n%s\n%s\n%s\n%s\n%s"
, "It was the best of times, it was the worst of times,"
, "it was the age of wisdom, it was the age of foolishness,"
, "it was the epoch of belief, it was the epoch of incredulity,"
, "it was the season of Light, it was the season of Darkness,"
, "it was the spring of hope, it was the winter of despair,"
, "we had everything before us, we had nothing before us"
);
Versus Java8 String.join():
String s = String.join("\n"
, "It was the best of times, it was the worst of times,"
, "it was the age of wisdom, it was the age of foolishness,"
, "it was the epoch of belief, it was the epoch of incredulity,"
, "it was the season of Light, it was the season of Darkness,"
, "it was the spring of hope, it was the winter of despair,"
, "we had everything before us, we had nothing before us"
);
If you want the newline for your particular system, you either need to use System.lineSeparator(), or you can use %n in String.format.
Another option is to put the resource in a text file, and just read the contents of that file. This would be preferable for very large strings to avoid unnecessarily bloating your class files.
In Eclipse if you turn on the option "Escape text when pasting into a string literal" (in Preferences > Java > Editor > Typing) and paste a multi-lined string whithin quotes, it will automatically add " and \n" + for all your lines.
String str = "paste your text here";
Stephen Colebourne has created a proposal for adding multi-line strings in Java 7.
Also, Groovy already has support for multi-line strings.
This is an old thread, but a new quite elegant solution (with only 4 maybe 3 little drawbacks) is to use a custom annotation.
Check :
A project inspired from that work is hosted on GitHub:
Example of Java code:
import org.adrianwalker.multilinestring.Multiline;
...
public final class MultilineStringUsage {
/**
<html>
<head/>
<body>
<p>
Hello<br/>
Multiline<br/>
World<br/>
</p>
</body>
</html>
*/
@Multiline
private static String html;
public static void main(final String[] args) {
System.out.println(html);
}
}
The drawbacks are
- that you have to activate the corresponding (provided) annotation processor.
- that String variable can not be defined as local variable Check Raw String Literals project where you can define variables as local variables
- that String cannot contains other variables as in Visual Basic .Net
with XML literal (
<%= variable %>) :-) - that String literal is delimited by JavaDoc comment (/**)
And you probably have to configure Eclipse/Intellij-Idea to not reformat automatically your Javadoc comments.
One may find this weird (Javadoc comments are not designed to embed anything other than comments), but as this lack of multiline string in Java is really annoying in the end, I find this to be the least worst solution.
JEP 378: Text Blocks covers this functionality and is included in JDK 15. It first appeared as JEP 355: Text Blocks (Preview) in JDK 13 and JEP 368: Text Blocks (Second Preview) in JDK 14 and can be enabled in these versions with the ––enable–preview javac option.
The syntax allows to write something like:
String s = """
text
text
text
""";
Previous to this JEP, in JDK 12, JEP 326: Raw String Literals aimed to implement a similar feature, but it was eventually withdrawn:
Please note: This was intended to be a preview language feature in JDK 12, but it was withdrawn and did not appear in JDK 12. It was superseded by Text Blocks (JEP 355) in JDK 13.
Another option may be to store long strings in an external file and read the file into a string.
This is something that you should never use without thinking about what it's doing. But for one-off scripts I've used this with great success:
Example:
System.out.println(S(/*
This is a CRAZY " ' ' " multiline string with all sorts of strange
characters!
*/));
Code:
// From:
// Takes a comment (/**/) and turns everything inside the comment to a string that is returned from S()
public static String S() {
StackTraceElement element = new RuntimeException().getStackTrace()[1];
String name = element.getClassName().replace('.', '/') + ".java";
StringBuilder sb = new StringBuilder();
String line = null;
InputStream in = classLoader.getResourceAsStream(name);
String s = convertStreamToString(in, element.getLineNumber());
return s.substring(s.indexOf("/*")+2, s.indexOf("*/"));
}
// From
private static String convertStreamToString(InputStream is, int lineNum) {
/*
* To convert the InputStream to String we use the BufferedReader.readLine()
* method. We iterate until the BufferedReader return null which means
* there's no more data to read. Each line will appended to a StringBuilder
* and returned as String.
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null; int i = 1;
try {
while ((line = reader.readLine()) != null) {
if (i++ >= lineNum) {
sb.append(line + "\n");
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
String.join
Java 8 added a new static method to java.lang.String which offers a slightly better alternative:
String.join(
CharSequence delimiter ,
CharSequence... elements
)
Using it:
String s = String.join(
System.getProperty("line.separator"),
"First line.",
"Second line.",
"The rest.",
"And the last!"
);
Java 13 and beyond
Multiline Strings are now supported in Java via Text Blocks. In Java 13 and 14, this feature requires you to set the ––enable–preview option when building and running your project. In Java 15 and later, this option is no longer required as Text Blocks have become a standard feature. Check out the official Programmer's Guide to Text Blocks for more details.
Now, prior to Java 13, this is how you'd write a query:
List<Tuple> posts = entityManager
.createNativeQuery(
"SELECT *\n" +
"FROM (\n" +
" SELECT *,\n" +
" dense_rank() OVER (\n" +
" ORDER BY \"p.created_on\", \"p.id\"\n" +
" ) rank\n" +
" FROM (\n" +
" SELECT p.id AS \"p.id\",\n" +
" p.created_on AS \"p.created_on\",\n" +
" p.title AS \"p.title\",\n" +
" pc.id as \"pc.id\",\n" +
" pc.created_on AS \"pc.created_on\",\n" +
" pc.review AS \"pc.review\",\n" +
" pc.post_id AS \"pc.post_id\"\n" +
" FROM post p\n" +
" LEFT JOIN post_comment pc ON p.id = pc.post_id\n" +
" WHERE p.title LIKE :titlePattern\n" +
" ORDER BY p.created_on\n" +
" ) p_pc\n" +
") p_pc_r\n" +
"WHERE p_pc_r.rank <= :rank\n",
Tuple.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", 5)
.getResultList();
Thanks to Java 13 Text Blocks, you can rewrite this query as follows:
List<Tuple> posts = entityManager
.createNativeQuery("""
SELECT *
FROM (
SELECT *,
dense_rank() OVER (
ORDER BY "p.created_on", "p.id"
) rank
FROM (
SELECT p.id AS "p.id",
p.created_on AS "p.created_on",
p.title AS "p.title",
pc.id as "pc.id",
pc.created_on AS "pc.created_on",
pc.review AS "pc.review",
pc.post_id AS "pc.post_id"
FROM post p
LEFT JOIN post_comment pc ON p.id = pc.post_id
WHERE p.title LIKE :titlePattern
ORDER BY p.created_on
) p_pc
) p_pc_r
WHERE p_pc_r.rank <= :rank
""",
Tuple.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", 5)
.getResultList();
Much more readable, right?