skiphoppy
skiphoppy

Reputation: 102941

Does Java have support for multiline strings?

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.

Upvotes: 649

Views: 806327

Answers (30)

Basil Bourque
Basil Bourque

Reputation: 340025

Language injection

Beyond Java’s Text Blocks feature covered in this correct Answer, your IDE may provide additional support for display and editing of multiline text in a language-savvy manner.

The IntelliJ IDE provides for language injections. See this article and video by Helen Scott. You can flag the embedded language by using either annotation or a comment.

Language injection works well with Java text blocks.

@Language ("SQL") 
String sql = """
    SELECT *
    FROM product_
    ;
    """;

IntelliJ will colorize the embedded language. And you can open a dedicated editor section to provide for language-appropriate editing. See this article.

Language injection works for SQL, JSON, RegExp, and more.

Other IDEs may provide a similar feature.

Upvotes: 1

alostale
alostale

Reputation: 790

Text Blocks

JEP 378: Text Blocks covers this functionality. This feature became a standard part of Java 15 and later.

It first appeared as JEP 355: Text Blocks (Preview) in JDK 13 and JEP 368: Text Blocks (Second Preview) in JDK 14 as preview feature.

The syntax allows writing 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 later withdrawn.

Upvotes: 190

Deltaplan
Deltaplan

Reputation: 9

I see at least one case where it should be avoided to use external files for long strings: if these long string are expected values in a unit-test file, because I think the tests should always be written in a way that they don't rely on any external resource.

Upvotes: 0

Yellow
Yellow

Reputation: 68

Saw all the answers and I think no one referred than in newer version of java you can do this:

String s = """
    This
    is 
    a
    multiline
    string
    """;
System.out.println(s);

This is what it prints:

This
is
a
multiline
string

Upvotes: 9

Kip
Kip

Reputation: 109473


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.

Upvotes: 581

Vlad Mihalcea
Vlad Mihalcea

Reputation: 154090

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?

IDE support

IntelliJ IDEA provides support for transforming legacy String concatenation blocks to the new multiline String format:

IntelliJ IDEA Text Blocks support

JSON, HTML, XML

The multiline String is especially useful when writing JSON, HTML, or XML.

Consider this example using String concatenation to build a JSON string literal:

entityManager.persist(
    new Book()
    .setId(1L)
    .setIsbn("978-9730228236")
    .setProperties(
        "{" +
        "   \"title\": \"High-Performance Java Persistence\"," +
        "   \"author\": \"Vlad Mihalcea\"," +
        "   \"publisher\": \"Amazon\"," +
        "   \"price\": 44.99," +
        "   \"reviews\": [" +
        "       {" +
        "           \"reviewer\": \"Cristiano\", " +
        "           \"review\": \"Excellent book to understand Java Persistence\", " +
        "           \"date\": \"2017-11-14\", " +
        "           \"rating\": 5" +
        "       }," +
        "       {" +
        "           \"reviewer\": \"T.W\", " +
        "           \"review\": \"The best JPA ORM book out there\", " +
        "           \"date\": \"2019-01-27\", " +
        "           \"rating\": 5" +
        "       }," +
        "       {" +
        "           \"reviewer\": \"Shaikh\", " +
        "           \"review\": \"The most informative book\", " +
        "           \"date\": \"2016-12-24\", " +
        "           \"rating\": 4" +
        "       }" +
        "   ]" +
        "}"
    )
);

You can barely read the JSON due to the escaping characters and the abundance of double quotes and plus signs.

With Java Text Blocks, the JSON object can be written like this:

entityManager.persist(
    new Book()
    .setId(1L)
    .setIsbn("978-9730228236")
    .setProperties("""
        {
           "title": "High-Performance Java Persistence",
           "author": "Vlad Mihalcea",
           "publisher": "Amazon",
           "price": 44.99,
           "reviews": [
               {
                   "reviewer": "Cristiano",
                   "review": "Excellent book to understand Java Persistence",
                   "date": "2017-11-14",
                   "rating": 5
               },
               {
                   "reviewer": "T.W",
                   "review": "The best JPA ORM book out there",
                   "date": "2019-01-27",
                   "rating": 5
               },
               {
                   "reviewer": "Shaikh",
                   "review": "The most informative book",
                   "date": "2016-12-24",
                   "rating": 4
               }
           ]
        }
        """
    )
);

Ever since I used C# in 2004, I've been wanting to have this feature in Java, and now we finally have it.

Upvotes: 49

ncmathsadist
ncmathsadist

Reputation: 4889

Java15 now supports triple-quoted strings a la Python.

Upvotes: 4

Chris T
Chris T

Reputation: 414

Two answers to this question:

  1. In you want to stick to pure Java, with Java 14 being released in March 2020, you can leverage the JEP 368 - Text Blocks, in Second Preview mode. Actually the feature is in preview mode in other releases (at least 13 has it). I created and example set here.
  2. While this feature is useful, it can be easily abused. Remember that Java requires compilation - having large character arrays in your code can be an easy way to shoot yourself in the leg (if you want a quick change, you will need recompilation - that toolset might not be available to the guy operating your application).

In my experience, it is advisable to keep large strings (and generally strings that could/should be altered at runtime by app operators) in configuration files.

Summary: use text blocks responsibly :).

Upvotes: 1

Naman
Naman

Reputation: 32036

With JDK/12 early access build # 12, one can now use multiline strings in Java as follows :

String multiLine = `First line
    Second line with indentation
Third line
and so on...`; // the formatting as desired
System.out.println(multiLine);

and this results in the following output:

First line
    Second line with indentation
Third line
and so on...

Edit: Postponed to java 13

Upvotes: 5

Lukasz Szozda
Lukasz Szozda

Reputation: 176114

Java 13 preview:

Text Blocks Come to Java. Java 13 delivers long-awaited multiline string by Mala Gupta

With text blocks, Java 13 is making it easier for you to work with multiline string literals. You no longer need to escape the special characters in string literals or use concatenation operators for values that span multiple lines.

Text block is defined using three double quotes (""") as the opening and closing delimiters. The opening delimiter can be followed by zero or more white spaces and a line terminator.

Example:

 String s1 = """
 text
 text
 text
 """;

Upvotes: 6

Mykhaylo Adamovych
Mykhaylo Adamovych

Reputation: 20976

    import org.apache.commons.lang3.StringUtils;

    String multiline = StringUtils.join(new String[] {
        "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",
        }, "\n");

Upvotes: 7

SRG
SRG

Reputation: 1579

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 : http://www.adrianwalker.org/2011/12/java-multiline-string.html

A project inspired from that work is hosted on GitHub:

https://github.com/benelog/multiline

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

  1. that you have to activate the corresponding (provided) annotation processor.
  2. that String variable can not be defined as local variable Check Raw String Literals project where you can define variables as local variables
  3. that String cannot contains other variables as in Visual Basic .Net with XML literal (<%= variable %>) :-)
  4. 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.

Upvotes: 103

user54579
user54579

Reputation: 4688

You can concatenate your appends in a separate method like:

public static String multilineString(String... lines){
   StringBuilder sb = new StringBuilder();
   for(String s : lines){
     sb.append(s);
     sb.append ('\n');
   }
   return sb.toString();
}

Either way, prefer StringBuilder to the plus notation.

Upvotes: 7

Peter Szanto
Peter Szanto

Reputation: 7722

Using this library

https://github.com/alessio-santacroce/multiline-string-literals

it is possible to write things like this

System.out.println(newString(/*
      Wow, we finally have
      multiline strings in
      Java! HOOO!
*/));

Very nice and easy, but works only for unit tests

Upvotes: 1

horace
horace

Reputation: 1311

I suggest using a utility as suggested by ThomasP, and then link that into your build process. An external file is still present to contain the text, but the file is not read at runtime. The workflow is then:

  1. Build a 'textfile to java code' utility & check into version control
  2. On each build, run the utility against the resource file to create a revised java source
  3. The Java source contains a header like class TextBlock {... followed by a static string which is auto-generated from the resource file
  4. Build the generated java file with the rest of your code

Upvotes: 3

Cjxcz Odjcayrwl
Cjxcz Odjcayrwl

Reputation: 22867

It may seem a little crazy, but since heredocs are syntactic sugar over one-line declarations with linebreaks escaped, one could write pre-processor for Java files that would change heredocs into single-liners during preprocessing.

It would require writing proper plugins for preprocessing files before compilation phase (for ant/maven build) and a plugin to IDE.

From an ideological point of view, it differs nothing from f.g. "generics", that is also a kind of pre-processed syntactic sugar over casting.

It's, however, a lot of work, so I would at your place just use .properties files.

Upvotes: 2

Morteza Adi
Morteza Adi

Reputation: 2473

I know this is an old question, however for intersted developers Multi line literals gonna be in #Java12

http://mail.openjdk.java.net/pipermail/amber-dev/2018-July/003254.html

Upvotes: 3

jpfreire
jpfreire

Reputation: 1278

Use Properties.loadFromXML(InputStream). There's no need for external libs.

Better than a messy code (since maintainability and design are your concern), it is preferable not to use long strings.

Start by reading xml properties:

 InputStream fileIS = YourClass.class.getResourceAsStream("MultiLine.xml");
 Properties prop = new Properies();
 prop.loadFromXML(fileIS);


then you can use your multiline string in a more maintainable way...

static final String UNIQUE_MEANINGFUL_KEY = "Super Duper UNIQUE Key";
prop.getProperty(UNIQUE_MEANINGFUL_KEY) // "\n    MEGA\n   LONG\n..."


MultiLine.xml` gets located in the same folder YourClass:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>
    <entry key="Super Duper UNIQUE Key">
       MEGA
       LONG
       MULTILINE
    </entry>
</properties>

PS.: You can use <![CDATA[" ... "]]> for xml-like string.

Upvotes: 5

Leo
Leo

Reputation: 1120

See Java Stringfier. Turns your text into a StringBuilder java block escaping if needed.

Upvotes: 7

icza
icza

Reputation: 418455

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!"
);

Upvotes: 61

user unknown
user unknown

Reputation: 36259

You may use scala-code, which is compatible to java, and allows multiline-Strings enclosed with """:

package foobar
object SWrap {
  def bar = """John said: "This is
  a test
  a bloody test,
  my dear." and closed the door.""" 
}

(note the quotes inside the string) and from java:

String s2 = foobar.SWrap.bar ();

Whether this is more comfortable ...?

Another approach, if you often handle long text, which should be placed in your sourcecode, might be a script, which takes the text from an external file, and wrappes it as a multiline-java-String like this:

sed '1s/^/String s = \"/;2,$s/^/\t+ "/;2,$s/$/"/' file > file.java

so that you may cut-and-paste it easily into your source.

Upvotes: 9

nurettin
nurettin

Reputation: 11756

In the IntelliJ IDE you just need to type:

""

Then position your cursor inside the quotation marks and paste your string. The IDE will expand it into multiple concatenated lines.

Upvotes: 17

Bruno Lee
Bruno Lee

Reputation: 1977

One good option.

import static some.Util.*;

    public class Java {

        public static void main(String[] args) {

            String sql = $(
              "Select * from java",
              "join some on ",
              "group by"        
            );

            System.out.println(sql);
        }

    }


    public class Util {

        public static String $(String ...sql){
            return String.join(System.getProperty("line.separator"),sql);
        }

    }

Upvotes: 4

Ruslan Ulanov
Ruslan Ulanov

Reputation: 976

It's not entirely clear from the question if author is interested in working with some sort of formatted large strings that need to have some dynamic values, but if that's the case a templating engine like StringTemplate (http://www.stringtemplate.org/) might be very useful.

A simple sample of the code that uses StringTemplate is below. The actual template ("Hello, < name >") could be loaded from an external plain text file. All indentation in the template will be preserved, and no escaping is necessary.

import org.stringtemplate.v4.*;
 
public class Hello {
    public static void main(String[] args) {
        ST hello = new ST("Hello, <name>");
        hello.add("name", "World");
        System.out.println(hello.render());
    }
}

P.S. It's always a good idea to remove large chunks of text from source code for readability and localization purposes.

Upvotes: 0

Rodney P. Barbati
Rodney P. Barbati

Reputation: 2100

Actually, the following is the cleanest implementation I have seen so far. It uses an annotation to convert a comment into a string variable...

/**
  <html>
    <head/>
    <body>
      <p>
        Hello<br/>
        Multiline<br/>
        World<br/>
      </p>
    </body>
  </html>
  */
  @Multiline
  private static String html;

So, the end result is that the variable html contains the multiline string. No quotes, no pluses, no commas, just pure string.

This solution is available at the following URL... http://www.adrianwalker.org/2011/12/java-multiline-string.html

Hope that helps!

Upvotes: 7

Andreas Dolk
Andreas Dolk

Reputation: 114817

A quite efficient and platform independent solution would be using the system property for line separators and the StringBuilder class to build strings:

String separator = System.getProperty("line.separator");
String[] lines = {"Line 1", "Line 2" /*, ... */};

StringBuilder builder = new StringBuilder(lines[0]);
for (int i = 1; i < lines.length(); i++) {
    builder.append(separator).append(lines[i]);
}
String multiLine = builder.toString();

Upvotes: 5

Fakeer
Fakeer

Reputation: 1044

I sometimes use a parallel groovy class just to act as a bag of strings

The java class here

public class Test {
    public static void main(String[] args) {
        System.out.println(TestStrings.json1);
        // consume .. parse json
    }
}

And the coveted multiline strings here in TestStrings.groovy

class TestStrings {
    public static String json1 = """
    {
        "name": "Fakeer's Json",
        "age":100,
        "messages":["msg 1","msg 2","msg 3"]
    }""";
}

Of course this is for static strings only. If I have to insert variables in the text I will just change the entire file to groovy. Just maintain strong-typing practices and it can be pulled off.

Upvotes: 1

David Pickett
David Pickett

Reputation: 7

Late model JAVA has optimizations for + with constant strings, employs a StringBuffer behind the scenes, so you do not want to clutter your code with it.

It points to a JAVA oversight, that it does not resemble ANSI C in the automatic concatenation of double quoted strings with only white space between them, e.g.:

const char usage = "\n"
"Usage: xxxx <options>\n"
"\n"
"Removes your options as designated by the required parameter <options>,\n"
"which must be one of the following strings:\n"
"  love\n"
"  sex\n"
"  drugs\n"
"  rockandroll\n"
"\n" ;

I would love to have a multi-line character array constant where embedded linefeeds are honored, so I can present the block without any clutter, e.g.:

String Query = "
SELECT
    some_column,
    another column
  FROM
      one_table a
    JOIN
      another_table b
    ON    a.id = b.id
      AND a.role_code = b.role_code
  WHERE a.dept = 'sales'
    AND b.sales_quote > 1000
  Order BY 1, 2
" ;

To get this, one needs to beat on the JAVA gods.

Upvotes: 2

Dan Lo Bianco
Dan Lo Bianco

Reputation: 31

If you like google's guava as much as I do, it can give a fairly clean representation and a nice, easy way to not hardcode your newline characters too:

String out = Joiner.on(newline).join(ImmutableList.of(
    "line1",
    "line2",
    "line3"));

Upvotes: 4

Monir
Monir

Reputation: 860

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";

Upvotes: 194

Related Questions