I created this class for being immutable and having a fluent API:
public final class Message {
public final String email;
public final String escalationEmail;
public final String assignee;
public final String conversationId;
public final String subject;
public final String userId;
public Message(String email, String escalationEmail, String assignee, String conversationId, String subject, String userId) {
this.email = email;
this.escalationEmail = escalationEmail;
this.assignee = assignee;
this.conversationId = conversationId;
this.subject = subject;
this.userId = userId;
}
public Message() {
email = "";
escalationEmail = "";
assignee = "";
conversationId = "";
subject = "";
userId = "";
}
public Message email(String e) { return new Message(e, escalationEmail, assignee, conversationId, subject, userId); }
public Message escalationEmail(String e) { return new Message(email, e, assignee, conversationId, subject, userId); }
public Message assignee(String a) { return new Message(email, escalationEmail, a, conversationId, subject, userId); }
public Message conversationId(String c) { return new Message(email, escalationEmail, assignee, c, subject, userId); }
public Message subject(String s) { return new Message(email, escalationEmail, assignee, conversationId, s, userId); }
public Message userId(String u) { return new Message(email, escalationEmail, assignee, conversationId, subject, u); }
}
My question is, will the optimizer be able to avoid lots of object creations when a new object is created like this:
Message m = new Message()
.email("foo@bar.com")
.assignee("bar@bax.com")
.subject("subj");
Is there anything to be gained from making a separate mutable builder object instead?
Update 2: After reading apangin's answer my benchmark is invalidated. I'll keep it here for reference of how not to benchmark :)
Update: I took the liberty of measuring this myself with this code:
public final class Message {
public final String email;
public final String escalationEmail;
public final String assignee;
public final String conversationId;
public final String subject;
public final String userId;
public static final class MessageBuilder {
private String email;
private String escalationEmail;
private String assignee;
private String conversationId;
private String subject;
private String userId;
MessageBuilder email(String e) { email = e; return this; }
MessageBuilder escalationEmail(String e) { escalationEmail = e; return this; }
MessageBuilder assignee(String e) { assignee = e; return this; }
MessageBuilder conversationId(String e) { conversationId = e; return this; }
MessageBuilder subject(String e) { subject = e; return this; }
MessageBuilder userId(String e) { userId = e; return this; }
public Message create() {
return new Message(email, escalationEmail, assignee, conversationId, subject, userId);
}
}
public static MessageBuilder createNew() {
return new MessageBuilder();
}
public Message(String email, String escalationEmail, String assignee, String conversationId, String subject, String userId) {
this.email = email;
this.escalationEmail = escalationEmail;
this.assignee = assignee;
this.conversationId = conversationId;
this.subject = subject;
this.userId = userId;
}
public Message() {
email = "";
escalationEmail = "";
assignee = "";
conversationId = "";
subject = "";
userId = "";
}
public Message email(String e) { return new Message(e, escalationEmail, assignee, conversationId, subject, userId); }
public Message escalationEmail(String e) { return new Message(email, e, assignee, conversationId, subject, userId); }
public Message assignee(String a) { return new Message(email, escalationEmail, a, conversationId, subject, userId); }
public Message conversationId(String c) { return new Message(email, escalationEmail, assignee, c, subject, userId); }
public Message subject(String s) { return new Message(email, escalationEmail, assignee, conversationId, s, userId); }
public Message userId(String u) { return new Message(email, escalationEmail, assignee, conversationId, subject, u); }
static String getString() {
return new String("hello");
// return "hello";
}
public static void main(String[] args) {
int n = 1000000000;
long before1 = System.nanoTime();
for (int i = 0; i < n; ++i) {
Message m = new Message()
.email(getString())
.assignee(getString())
.conversationId(getString())
.escalationEmail(getString())
.subject(getString())
.userId(getString());
}
long after1 = System.nanoTime();
long before2 = System.nanoTime();
for (int i = 0; i < n; ++i) {
Message m = Message.createNew()
.email(getString())
.assignee(getString())
.conversationId(getString())
.escalationEmail(getString())
.subject(getString())
.userId(getString())
.create();
}
long after2 = System.nanoTime();
System.out.println("no builder : " + (after1 - before1)/1000000000.0);
System.out.println("with builder: " + (after2 - before2)/1000000000.0);
}
}
I found the difference to be significant (builder is faster) if the string arguments are not new objects, but all the same (see commented code in getString)
In what I imagine is a more realistic scenario, when all the strings are new objects, the difference is negligible, and the JVM startup would cause the first one to be a tiny bit slower (I tried both ways).
With the "new String" the code was in total many times slower as well (I had to decrease the n), perhaps indicating that there is some optimization of the "new Message" going on, but not of the "new String".