Shame they don't actually include templating inside of the string literal. Still have to append with a lot of tokenized replaces. Since they've committed to no formatting in the literal section, will be interesting to see if they eventually support a syntax like python's f-strings: f"""${foo}"""
Another alternative involves the introduction of a new instance method, String::formatted, which could be used as follows:
String source = """
public void print(%s object) {
System.out.println(Objects.toString(object));
}
""".formatted(type);
I'd still prefer honest-to-goodness string interpolation. Formatting functions with positional placeholders need a linter to be kept (vaguely) maintainable, and I suspect that formatting functions with named placeholders would be difficult to make acceptably ergonomic or performant in Java.
As far as I can tell, there is no performance degradation from the recursive interpolation; the editor reads, interpolates key/value pairs, and substitutes the resulting values into R Markdown documents riddled with up to a thousand simple formulas in about 600 milliseconds (on my desktop computer).
Also, the editor provides a way to maintain the interpolated variables in a tree-like view:
Maybe A, but not (IMO) dramatically so. But shouldn't A actually be "${cat} ${dog} ${giraffe} <random text> ... <random text> ${dinosaur}"? That makes it closer in difficulty to B.
To me, the difference shows up more with this scenario:
A: "${v1} ${v2} ${v3} ${v4}"
B1: "%s %s %s %s".formatted(v1, v2, v3)
B2: "%s %s %s %s".formatted(v1, v2, v3, v4, v5)
B3: "%s %s %s".formatted(v1, v2, v3, v4)
A does what you tell it, whether it's wrong or right. B has a chance of warning you that the argument list and the format specifier don't match. On the other hand, B gives you two things that have to be kept in sync with each other, and A can't get out of sync, since there's only one thing.
So: Less of a chance to make the error, or more of a chance to catch it. Which is better? I lean toward A, but I will admit that it seems subjective.
Go doesn't have interpolated strings but printf / sprintf and co, and its compiler will warn and error if your arguments do not line up or have the wrong type.
I mean it's not ideal, but sprintf and co are only intended for relatively short text (like logging), if you have any more, use a templating language instead. Plenty of those in Java as well (JSP, Velocity, etc).
while cool, it's pretty telling that similarly high-level languages like C#, Kotlin, Scala, Python, Swift and Javascript have had this widely-used quality of life for years while Java doesn't
And by telling I think you mean that over time Java has very thoughtfully evolved in a way that respects backwards compatibility, or that once a feature is in the language it'll very likely stay there for the next 25 years. Yes, innovation is very important (and happening faster than ever now with the 6-month release cadence), but a majority of the work isn't in deciding what goes in, it's deciding what stays out and if something does go in how does it satisfy both audiences of "move slow don't break" and "move faster we want features".
I'm glad Java is being conservative and not doing that. JS/Python f-strings have not proven themselves long-term. It also bloats the language with yet another mini-DSL. You can always add a proper templating engine on top.
Java relies on String + Object for composing strings, thus unsafely converting any object into string. An implicit conversion, akin to what JavaScript is doing.
String interpolation would be safer, as it would make the intent clearer, and you wouldn't risk bumping in corner cases. It would be even safer if the protocol could be overrided such that you wouldn't have to use Object#toString, but for Java that's too much already.
Java has been a very conservative language. And I understand why.
But it's funny how, for many features that were added later, people were rationalizing their absence with such lines too. E.g. we don't need anonymous functions / lambdas, as anonymous classes are enough. Well, turned out that Microsoft was right all along when they released those in J#.
Also adding a template engine is overkill for doing string concatenation.
> But it's funny how, for many features that were added later, people were rationalizing their absence with such lines too
There's nothing wrong with that. If string interpolation is really useful, then Java will add it some years down the line.
It's cheap to add features but literally impossible to remove them. There's no way to undo a mistake in language design. That's an asymmetry. I'd rather err on the side of caution than kitchen-sinking it.
Oh, I don't know about that. I wouldn't call it impossible.
I like how Java binaries still work on the latest Java, however, if distribution happens via binaries, why should the language keep source compatibility anyway?
Or, you know, the latest compiler could allow you to select the source code version you want. And automated code migration tools can work too.
---
Err'ing on the side of not getting features is why Java has lost a lot of mindshare.
Java is still super popular, but that's basically in spite of the language itself, because the language is awful.
> JS/Python f-strings have not proven themselves long-term.
Are you saying that they haven't been around for long or that they failed? If latter, then I respectfully disagree. Any recent JS and Python code I have seen uses them extensively. They are also a joy to use, and imho improve readability immensely. Ergonomics matter. This is one aspect of Python3 I would miss the most if I had to go back to 2.
The ones in Python work significantly differently though; they have inline code execution (rather than using a varargs list after the f-string itself).
As I recall (and Wikipedia seems to confirm), parameter substitutions were in the original bourne shell in 1979, so... Yeah, I'm not sure what's going on there.
I was going to answer gp "probably not" - but I would've been wrong. It looks to me like Java now has some of the most sophisticated in-line multi-line strings/here-documents that I'm aware of in any language. More details in:
I'm not entirely sure "most sophisticated" is entirely a good thing - but the jep looks pretty thorough at least.
Ed: how does Scala do it? Java uses the indents for the closing triple-quote to determine stripping/indent + cutting trailing spaces. Ie, if my reading is correct:
a = """
OK.
"""
Is "OK.\n" (even if there are any spaces after the dot).
It helps that Java is exclusively a compiled programming language, so these are just syntactic sugar concerns for the parser/lexer and the compiled bytecode ends up looking identical to having just used one long string with inline \n.
So given that, I don't think it really ends up being that sophisticated. I think this could be a decent easy programming question for a technical interview though!
IntelliJ shows you exactly where the block starts, so if you have a line with less indentation (causing the entire block to start at that column), it is easy to spot.
Thanks for sharing that. I can definitely see myself using the multi-line blocks since they are much better than the kind of things we need to do today.
At the same time I'm slightly scared of that magic. It helps that it's well-defined in the JEP.
Ultimately the issue isn't the presence or absence of those classes (because they aren't hard to write minimal versions of as a one-off if you need them and can't allow the dependency), it's the pervasiveness of use (or lack thereof) by libraries and especially the standard library.
If Try and Either were added to the Java stdlib tomorrow, the rest of the stdlib would still be stuck throwing exceptions for error propagation, because they can't change the return types of existing methods. The only benefit would be that third-party libraries could feel comfortable enough to use them in their type signatures. But, in practice, most libraries still target Java 8, so I wouldn't hold my breath for that, either.
I think we can do better than Java, Scala and Kotlin. All of these ended up to be piles of abstractions and complexity. Scala went too hardcore for somebody that just wants to deliver. Kotlin gives you anxiety everytime they push for supporting a new platform. Java, though it is still nice, has accumulated too much cruft during the years. I wish for a Go-like language on the JVM. A modern language that offers you a minimum number of features from which you can build upon. Moreover, lower, well-thought out abstractions would give a boost in perfomance with the new garbage collectors that have been added in the JVM. I can imagine Clojure is something that I would like to try again sometimes, but the fact that it is a LISP and lacks even simple types make it less attractive to the majority of devs. Also, Clojure wouldn't be my choice for high-perfomance if I want to do mostly imperative stuff.
Scala is perfect for just delivering, myself and my business partner have been using it to rapidly prototype and build for a decade now. You just avoid all the stuff that tries to make scala into haskell and use it as the hybrid OO/functional language is was designed as.
You're lucky that you can do that kind of work. I've been working for a state company in Bulgaria and we had to maintain some Scala pipelines built with Apache Spark some years ago. It wasn't a pleasant experience, just a sea of vars/vals and cases of reinventing the wheel only to use some functional constructs when it could be simply done in Java by just adding a dependency that has already solved that problem like 10 years ago. It wasn't even some complicated logic in that, just calling different APIs and playing with dataframes, but the fact that they hid from me so much behavior only to reduce verbosity gave me some stressful days.
And if you need it, static typing is one annotation away... This is such an underestimated feature. While prototyping it might be easier to use dynamic typing, but once a project becomes complex and has more people working on it etc, - you might want to switch it to statically-typed. Groovy simplifies this immensely and minimizes the amount of code that needs to rewritten.