Parameterized Strings in Java - java

I find myself very frequently wanting to write reusable strings with parameter placeholders in them, almost exactly like what you'd find in an SQL PreparedStatement.
Here's an example:
private static final String warning = "You requested ? but were assigned ? instead.";
public void addWarning(Element E, String requested, String actual){
warning.addParam(0, requested);
warning.addParam(1, actual);
e.setText(warning);
//warning.reset() or something, I haven't sorted that out yet.
}
Does something like this exist already in Java? Or, is there a better way to address something like this?
What I'm really asking: is this ideal?

String.format()
Since Java 5, you can use String.format to parametrize Strings. Example:
String fs;
fs = String.format("The value of the float " +
"variable is %f, while " +
"the value of the " +
"integer variable is %d, " +
" and the string is %s",
floatVar, intVar, stringVar);
See http://docs.oracle.com/javase/tutorial/java/data/strings.html
Alternatively, you could just create a wrapper for the String to do something more fancy.
MessageFormat
Per the comment by Max and answer by Affe, you can localize your parameterized String with the MessageFormat class.

You could use String.format. Something like:
String message = String.format("You requested %2$s but were assigned %1$s", "foo", "bar");
will generate
"You requested bar but were assigned foo"

It is built-in, yes. The class you're looking for is java.text.MessageFormat

Java String formatter

The String class provides the following format method, http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/String.html. For example (as per the original post):
private final static String string = "You requested %s but were assigned %s instead.";
public void addWarning(Element e, String requested, String actual) {
String warning = String.format(string, requested, actual);
e.setText(warning);

I would probably do something like:
private final String warning = String.format("You requested %s but were assigned %s instead.", requested, actual);
If you wanted to put the parameterized string before the call to format the string you could do something like what you see below, although this is less clear.
Neither of these solutions are inherently localizeable; you may want to consider something like a .properties file if you wish to support non-English locales.
private static final String warning = "You requested %s but were assigned %s instead.";
public void addWarning(Element E, String requested, String actual){
e.setText(String.format(warning, requested, actual);
//warning.reset() or something, I haven't sorted that out yet.
}

the formatter can do this for you (with some extras as adding leading zeroes spacing things out and more)
private static final String warning = "You requested %1$s but were assigned %2$s instead.";
public void addWarning(Element E, String requested, String actual){
Formatter f = new Formatter();//you'll need to recreate it each time
try{
f.format(warning,requested,actual);
e.setText(f.out().toString());
}finally{f.close();}
}

Since Java 15, String has a method formatted equivalent to String.format.
So you can directly use "var1 is %s, var2 is %s".formatted(var1, var2);
Java documentation

Well if your String was final you sure wouldn't be able to modify it later. I don't know if maybe you could find a better use case for that kind of thing as you could simply do:
public void addWarning(Element E, String requested, String actual){
String warning = "You requested" + requested + " but were assigned " + actual + " instead."
e.setText(warning);
}

Related

Java/Kotlin equivalent to C#'s template string [duplicate]

Primitive question, but how do I format strings like this:
"Step {1} of {2}"
by substituting variables using Java? In C# it's easy.
Take a look at String.format. Note, however, that it takes format specifiers similar to those of C's printf family of functions -- for example:
String.format("Hello %s, %d", "world", 42);
Would return "Hello world, 42". You may find this helpful when learning about the format specifiers. Andy Thomas-Cramer was kind enough to leave this link in a comment below, which appears to point to the official spec. The most commonly used ones are:
%s - insert a string
%d - insert a signed integer (decimal)
%f - insert a real number, standard notation
This is radically different from C#, which uses positional references with an optional format specifier. That means that you can't do things like:
String.format("The {0} is repeated again: {0}", "word");
... without actually repeating the parameter passed to printf/format. (see The Scrum Meister's comment below)
If you just want to print the result directly, you may find System.out.printf (PrintStream.printf) to your liking.
In addition to String.format, also take a look java.text.MessageFormat. The format less terse and a bit closer to the C# example you've provided and you can use it for parsing as well.
For example:
int someNumber = 42;
String someString = "foobar";
Object[] args = {new Long(someNumber), someString};
MessageFormat fmt = new MessageFormat("String is \"{1}\", number is {0}.");
System.out.println(fmt.format(args));
A nicer example takes advantage of the varargs and autoboxing improvements in Java 1.5 and turns the above into a one-liner:
MessageFormat.format("String is \"{1}\", number is {0}.", 42, "foobar");
MessageFormat is a little bit nicer for doing i18nized plurals with the choice modifier. To specify a message that correctly uses the singular form when a variable is 1 and plural otherwise, you can do something like this:
String formatString = "there were {0} {0,choice,0#objects|1#object|1<objects}";
MessageFormat fmt = new MessageFormat(formatString);
fmt.format(new Object[] { new Long(numberOfObjects) });
String#format
The most frequent way to format a String is using this static method, that is long available since Java 5 and has two overloaded methods:
String#format(String format, Object args...)
String#format(Locale l, String format, Object args...)
The method is easy to use and the format pattern is defined by underlying formatter.
String step1 = "one";
String step2 = "two";
// results in "Step one of two"
String string = String.format("Step %s of %s", step1, step2);
You can pass a Locale to respect the language and regional specification. Refer to this answer for more information: https://stackoverflow.com/a/6431949/3764965 (credits to Martin Törnwall).
MessageFormat
The MessageFormat class is available since the first version of Java and is suitable for internationalization. In the simplest form, there is a static method for formatting:
MessageFormat#format​(String pattern, Object... arguments)
String step1 = "one";
String step2 = "two";
// results in "Step one of two"
String string = MessageFormat.format("Step {0} of {1}", step1, step2);
Remember MessageFormat follows a specific pattern different from String#format, refer to its JavaDoc for more details: MessageFormat - patterns.
It is possible to use Locale, however, one has to instantiate the object of the class and pass it to the constructor since the static method above uses the default constructor with the default locale. Refer to this answer for more information: https://stackoverflow.com/a/6432100/3764965 (credits to ataylor).
Non standard JDK solutions
There are plenty of ways to format Strings using external libraries. They add little to no benefit if the libraries are imported solely for the purpose of String formatting. Few examples:
Apache Commons: StringSubstitutor, examples in its JavaDoc.
Cactoos: FormattedText, examples here.
Interestingly, Guava doesn't plan to add formatting or templating features: #1142.
... and other custom implementations.
Feel free to add more, however, I find no reason to further expand this section.
Alternative since Java 15
There is a new instance method called String#formatted(Object... args) as of Java 15.
The internal implementation is the same as String#format(String format, Object... args).
Formats using this string as the format string, and the supplied arguments.
String step1 = "one";
String step2 = "two";
// results in "Step one of two"
String string = "Step %s of %s".formatted(step1, step2);
Advantage: The difference is that the method is not static and the formatting pattern is a string itself from which a new one is created based on the args. This allows chaining to build the format itself first.
Disadvantage: There is no overloaded method with Locale, therefore uses the default one. If you need to use a custom Locale, you have to stick with String#format(Locale l, String format, Object... args).
If you choose not to use String.format, the other option is the + binary operator
String str = "Step " + a + " of " + b;
This is the equivalent of
new StringBuilder("Step ").append(String.valueOf(1)).append(" of ").append(String.valueOf(2));
Whichever you use is your choice. StringBuilder is faster, but the speed difference is marginal. I prefer to use the + operator (which does a StringBuilder.append(String.valueOf(X))) and find it easier to read.
I've wrote my simple method for it :
public class SomeCommons {
/** Message Format like 'Some String {0} / {1}' with arguments */
public static String msgFormat(String s, Object... args) {
return new MessageFormat(s).format(args);
}
}
so you can use it as:
SomeCommons.msfgFormat("Step {1} of {2}", 1 , "two");
public class StringFormat {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("================================");
for(int i=0;i<3;i++){
String s1=sc.next();
int x=sc.nextInt();
System.out.println(String.format("%-15s%03d",s1,x));
}
System.out.println("================================");
}
}
outpot
"================================"
ved15space123
ved15space123
ved15space123
"================================
Java solution
The "-" is used to left indent
The "15" makes the String's minimum length it takes up be 15
The "s" (which is a few characters after %) will be substituted by our String
The 0 pads our integer with 0s on the left
The 3 makes our integer be a minimum length of 3
This solution worked for me. I needed to create urls for a REST client dynamically so I created this method, so you just have to pass the restURL like this
/customer/{0}/user/{1}/order
and add as many params as you need:
public String createURL (String restURL, Object ... params) {
return new MessageFormat(restURL).format(params);
}
You just have to call this method like this:
createURL("/customer/{0}/user/{1}/order", 123, 321);
The output
"/customer/123/user/321/order"
I wrote this function it does just the right thing. Interpolate a word starting with $ with the value of the variable of the same name.
private static String interpol1(String x){
Field[] ffield = Main.class.getDeclaredFields();
String[] test = x.split(" ") ;
for (String v : test ) {
for ( Field n: ffield ) {
if(v.startsWith("$") && ( n.getName().equals(v.substring(1)) )){
try {
x = x.replace("$" + v.substring(1), String.valueOf( n.get(null)));
}catch (Exception e){
System.out.println("");
}
}
}
}
return x;
}
Apache Commons StringSubstitutor provides a simple and readable way to do format Strings with named variables.
import org.apache.commons.text.StringSubstitutor;
// ...
Map<String, String> values = new HashMap<>();
values.put("animal", "quick brown fox");
values.put("target", "lazy dog");
StringSubstitutor sub = new StringSubstitutor(values);
String result = sub.replace("The ${animal} jumped over the ${target}.");
// "The quick brown fox jumped over the lazy dog."
This class supports providing default values for variables.
String result = sub.replace("The number is ${undefined.property:-42}.");
// "The number is 42."
To use recursive variable replacement, call setEnableSubstitutionInVariables(true);.
Map<String, String> values = new HashMap<>();
values.put("b", "c");
values.put("ac", "Test");
StringSubstitutor sub = new StringSubstitutor(values);
sub.setEnableSubstitutionInVariables(true);
String result = sub.replace("${a${b}}");
// "Test"
The org.apache.commons.text.StringSubstitutor helper class from Apache Commons Text provides named variable substitution
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String resolved = new StringSubstitutor(valuesMap).replace("The ${animal} jumped over the ${target}.");
System.out.println(resolved); // The quick brown fox jumped over the lazy dog.

Java - String.format , skip if null [duplicate]

Consider the custom toString() implementation of a bean:
#Override
public String toString() {
String.format("this is %s", this.someField);
}
This yields this is null if someField is null.
Is there a way to override the default null string representation of null-valued arguments to another text, i.e., ? without calling explicitly replaceAll(...) in the toString method?
Note: The bean inherits from a superclass that could implement Formattable (http://docs.oracle.com/javase/7/docs/api/java/util/Formattable.html) but I just don't seem to understand how to make this work.
EDIT: The snippet is over-simplified for the sake of example but I'm not looking for ternary operator solutions someField==null ? "?" : someField because:
there can be (potentially) a great many fields involved in toString() so checking all fields is too cumbersome and not fluent.
other people whom I have little control over (if any) are writing their own subclasses.
if a method is called and returns null that would either imply calling the method twice or declaring a local variable.
Rather, can anything be done using the Formattable interface or having some custom Formatter (which is final btw.)?
With java 8 you can now use Optional class for this:
import static java.util.Optional.ofNullable;
...
String myString = null;
System.out.printf("myString: %s",
ofNullable(myString).orElse("Not found")
);
For a Java 7 solution that doesn't require external libraries:
String.format("this is %s", Objects.toString(this.someField, "?"));
The nicest solution, in my opinion, is using Guava's Objects method, firstNonNull. The following method will ensure you will print an empty string if someField is ever null.
String.format("this is %s", MoreObjects.firstNonNull(this.someField, ""));
Guava docs.
A bit late on the subject, but this could be a quite clean-looking solution :
First, create your own format method...
private static String NULL_STRING = "?";
private static String formatNull(String str, Object... args){
for(int i = 0; i < args.length; i++){
if(args[i] == null){
args[i] = NULL_STRING;
}
}
return String.format(str, args);
}
Then, use it as will...
#Test
public void TestNullFormat(){
Object ob1 = null;
Object ob2 = "a test";
String str = formatNull("this is %s", ob1);
assertEquals("this is ?", str);
str = formatNull("this is %s", ob2);
assertEquals("this is a test", str);
}
This eliminates the need for multiple, hard-to-read, ternary operators.
If you don't want to use replaceAll(), You can assign a default text(String) for someField.
But if some time this may assign null again. So you can use validation for that case
this.someField == null ? "defaultText" : this.someField
To avoid repeating ternary operator you can wrap it in more readable method that will check if your object is null and return some default value if it is true like
static <T> T changeNull(T arg, T defaultValue) {
return arg == null ? defaultValue : arg;
}
usage
String field = null;
Integer id = null;
System.out.printf("field is %s %n", changeNull(field, ""));
System.out.printf("id is %d %n", changeNull(id, -1));
System.out.printf("id is %s %n", changeNull(field, ""));
output:
field is
id is -1
id is
You could just do
String.format("this is %s", (this.someField==null?"DEFAULT":this.someField));
From java 7, you can use Objects.toString(Object o, String nullDefault).
Applied to your example: String.format("this is %s", Objects.toString(this.someField, "?"));
public static String format(String format, Object... args){
for (int i=0;i<args.length;i++){
if (args[i]==null) args[i]="";
}
return String.format(format,args);
}
then use the method ,ok
To keep the original value of someField (in case null is a valid value), you can use a ternary operator.
String.format("This is %s", (this.someField == null ? "unknown" : this.someField));

How to modify void class method so it returns the required information

I wrote a class that has a whole collection of static methods. One of my static methods accepts a string as an argument. I'm supposed to send a name to the method such as John Mike Baldibanga and its supposed to return a string containing only the initials of the first two names and the full last name.
so if I send John Mike Baldibanga to my method I want it to return
J M Baldibanga.
So far I was only able to figure out how to get the method to work by using a void.
public static void Theinitials(String z){
String s []= z.split(" ");
System.out.print(s[0].charAt(0) + " ");
System.out.print(s[1].charAt(0)+ " ");
System.out.println(s[2]);
}
This is what I use to call the method presently:
String.Theinitials(name);
it's supposed to return a string
So return a String instead of void and instead of System.out.printing, build up a single String result and return that:
public static String theInitials(String z){
String s []= z.split(" ");
String result = s[0].charAt(0) + " " + s[1].charAt(0) + " " + s[2];
return result;
}
Then print it later if that's what you need to do:
String initials = WhatEverYourClassIsCalled.theInitials(name);
System.out.println(initials);
From String.Theinitials(name); it looks like your class is called String. That might cause you some headaches, I would suggest renaming it.
Also notice how I called the method theInitials, this is called lowerCamelCase and is just good practice when naming methods and variables in Java.
A void method is a method with no return type. I think you are trying to return a string, Simply change the methods return type like so:
public static String Theinitials(String z){
String s []= z.split(" ");
String str;
str = (s[0].charAt(0) + " ");
str = (str + (s[1].charAt(0)+ " "));
str = (str + s[2]);
return str;
}
This should make a string str which will be made out of the string you would have exported to the console. You then return that string.
So far I was only able to figure out how to get the method to work by
using a void.
void is not supposed to return any value at all.
If you really want to know a method to work by using a void, declare a private static member in the class and assign the value to be returned to this static variable. [Although this is not highly recommended but since you asked]
Best choice is to let your method return String instead.

How does System.out.print() work?

I have worked with Java for a quite a long time, and I was wondering how the function System.out.print() works.
Here is my doubt:
Being a function, it has a declaration somewhere in the io package. But how did Java developers do that, since this function can take in any number of arguments and any argument types no matter how they are arranged? e.g:
System.out.print("Hello World");
System.out.print("My name is" + foo);
System.out.print("Sum of " + a + "and " + b + "is " + c);
System.out.print("Total USD is " + usd);
No matter what is the datatype of variables a, b, c, usd, foo or how they are passed, System.out.print() never throws an error.
For me, I have never worked on any project where the requirement was like this. Provided, if I get a requirement like this, I really don't know how to solve it.
Can anyone explain to me how it's done?
System.out is just an instance of PrintStream. You can check its JavaDoc. Its variability is based on method overloading (multiple methods with the same name, but with different parameters).
This print stream is sending its output to so called standard output.
In your question you mention a technique called variadic functions (or varargs). Unfortunately that is not supported by PrintStream#print, so you must be mistaking this with something else. However it is very easy to implement these in Java. Just check the documentation.
And if you are curious how Java knows how to concatenate non-string variables "foo" + 1 + true + myObj, it is mainly responsibility of a Java compiler.
When there is no variable involved in the concatenation, the compiler simply concatenates the string. When there is a variable involved, the concatenation is translated into StringBuilder#append chain. There is no concatenation instruction in the resulting byte code; i.e. the + operator (when talking about string concatenation) is resolved during the compilation.
All types in Java can be converted to string (int via methods in Integer class, boolean via methods in Boolean class, objects via their own #toString, ...). You can check StringBuilder's source code if you are interested.
UPDATE: I was curious myself and checked (using javap) what my example System.out.println("foo" + 1 + true + myObj) compiles into. The result:
System.out.println(new StringBuilder("foo1true").append(myObj).toString());
Even though it look as if System.put.print...() take a variable number of arguments it doesn't. If you look closely, the string is simply concatenated and you can do the same with any string. The only thing that happens is, that the objects you are passing in, are implicitily converted to a string by java calling the toString() method.
If you try to do this it will fail:
int i = 0;
String s = i;
System.out.println(s);
Reason is, because here the implicit conversion is not done.
However if you change it to
int i = 0;
String s = "" + i;
System.out.println(s);
It works and this is what happens when using System.put.print...() as well.
If you want to implement a variable number of arguments in java to mimimc something like C printf you can declare it like this:
public void t(String s, String ... args)
{
String val = args[1];
}
What happens here is that an array of Strings is passed in, with the length of the provided arguments. Here Java can do the type checking for you.
If you want truly a printf then you have to do it like this:
public void t(String s, Object ... args)
{
String val = args[1].toString();
}
Then would you have to cast or interpret the arguments accordingly.
It is a very sensitive point to understand how System.out.print works.
If the first element is String then plus(+) operator works as String concate operator. If the first element is integer plus(+) operator works as mathematical operator.
public static void main(String args[]) {
System.out.println("String" + 8 + 8); //String88
System.out.println(8 + 8+ "String"); //16String
}
Evidently, the compiler was made in a confusing way although the compiler developers thought they added some smartness. The true smartness they should really add is to look entire argument and interpret + operator consistently. For example, System.out.println(1+2+"hello"+3+4); should output 3hello7 instead of 3hello34
I think you are confused with the printf(String format, Object... args) method. The first argument is the format string, which is mandatory, rest you can pass an arbitrary number of Objects.
There is no such overload for both the print() and println() methods.
Its all about Method Overloading.
There are individual methods for each data type in println() method
If you pass object :
Prints an Object and then terminate the line. This method calls at first String.valueOf(x) to get the printed object's string value, then behaves as though it invokes print(String) and then println().
If you pass Primitive type:
corresponding primitive type method calls
if you pass String :
corresponding println(String x) method calls
You can convert anything to a String as long as you choose what to print. The requirement was quite simple since Objet.toString() can return a default dumb string: package.classname + # + object number.
If your print method should return an XML or JSON serialization, the basic result of toString() wouldn't be acceptable. Even though the method succeed.
Here is a simple example to show that Java can be dumb
public class MockTest{
String field1;
String field2;
public MockTest(String field1,String field2){
this.field1=field1;
this.field2=field2;
}
}
System.out.println(new MockTest("a","b");
will print something package.Mocktest#3254487 ! Even though you only have two String members and this could be implemented to print
Mocktest#3254487{"field1":"a","field2":"b"}
(or pretty much how it appears in the debbuger)
#ikis, firstly as #Devolus said these are not multiple aruements passed to print(). Indeed all these arguments passed get
concatenated to form a single String. So print() does not teakes multiple arguements (a. k. a. var-args). Now the concept that remains to discuss is how print() prints any type of the arguement passed
to it.
To explain this - toString() is the secret:
System is a class, with a static field out, of type PrintStream. So you're calling the println(Object x) method of a
PrintStream.
It is implemented like this:
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
As wee see, it's calling the String.valueOf(Object) method. This is implemented as follows:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
And here you see, that toString() is called.
So whatever is returned from the toString() method of that class, same gets printed.
And as we know the toString() is in Object class and thus inherits a default iplementation from Object.
ex: Remember when we have a class whose toString() we override and then we pass that ref variable to print, what do you see printed? - It's what we return from the toString().
The scenarios that you have mentioned are not of overloading, you are just concatenating different variables with a String.
System.out.print("Hello World");
System.out.print("My name is" + foo);
System.out.print("Sum of " + a + "and " + b + "is " + c);
System.out.print("Total USD is " + usd);
in all of these cases, you are only calling print(String s) because when something is concatenated with a string it gets converted to a String by calling the toString() of that object, and primitives are directly concatenated.
However if you want to know of different signatures then yes print() is overloaded for various arguments.

using enums or static string variable in java

got a question around which one is better to use. Java5 Enums or static string.
I always get the data in form of Strings.
So for example,
private static final String LOAD_DRUGS = "load drugs";
or use Java5 enums
public enum LoadType
{
LOAD_DRUGS("load drugs");
}
In my code, I always receive "load drugs" kind of strings. I use if else statements to see what to do next based on it. But I am tending to use java5 enums and use switch case, but I always have to get the enum based of the string value I get.
So what are the pro's and con's of both ways??
Thanks!!
This answer is probably overkill. Maybe there's a badge for that. Anyway, it could be useful in a situation in which you have a lot of enumeration values and have to deal with a Strings as being the means by which another system sends information to you. That is exactly what I have (something north of 50), so I used this construct so that I could generate a mapping just once of the Strings reported by the db and the enums I used internally, and then not think about it after -- toString and fromString do all the work:
package com.stevej;
import com.google.common.collect.HashBiMap;
public enum TestEnum {
ALPHA("Alpha"), BETA("Beta"), GAMMA("Gamma");
private static HashBiMap<TestEnum, String> stringMapping = HashBiMap
.create(TestEnum.values().length);
private String stringValue = null;
TestEnum(String stringValue) {
this.stringValue = stringValue;
}
String getStringValue() {
return this.stringValue;
}
#Override
public String toString() {
return stringMapping.get(this);
}
public static TestEnum fromString(String string) {
return stringMapping.inverse().get(string);
}
static {
for (TestEnum e : TestEnum.values()) {
stringMapping.put(e, e.getStringValue());
}
}
}
Here's a quick test to show the data switching back and forth:
package com.stevej;
public class StackOverflowMain {
public static void main(String[] args) {
System.out.println(">> " + TestEnum.ALPHA);
System.out.println(">> " + TestEnum.BETA);
System.out.println(">> " + TestEnum.GAMMA);
TestEnum a = TestEnum.fromString("Alpha");
TestEnum b = TestEnum.fromString("Beta");
TestEnum c = TestEnum.fromString("Gamma");
System.out.println(">> " + a);
System.out.println(">> " + b);
System.out.println(">> " + c);
}
}
The output shows the use of the mixed case values instead of the uppercase, showing my strings are being used:
>> Alpha
>> Beta
>> Gamma
>> Alpha
>> Beta
>> Gamma
Note that I am using the Google Guava library so I can take advantage of the BiMap.
you can try a simple substitution for turn the string into an enum
switch(MyEnum.valueOf(text.replace(' ', '_')) {
case load_strings:
You can use toUpperCase() if you want it in upper case.
You should do what you think is the simplest and clearest.
Generally you should use Enums to store a set of values that are related in some way. They all should have a similar "is a" relationship. It should not be used to simply store a set of constant strings that are not related.
Of course, if you have a switch statement over a set of string values then that suggests that those string values are related and should be grouped as an Enum and use enum's mechanism to use a switch.
The enum type's valueOf method will allow you to convert from a String to the enum type if the passed string is equal to the name value. If this is not the case, you could implement your own valueOf that uses a stored string field rather than the name. You could store the fields in a private static Map for the conversion.
Because enum is typed.
Static String is not typed.
public enum Something{
CAT("cat");
private String animal;
private Something(String animal){
this.animal = animal;
}
public String getAnimal(){
return this.animal;
}
}

Categories