Method chaining is a convenience mechanism that allows multiple method invocations on the same object to occur in a single statement. Classes that support In method chaining provide several setter methods that return the this
reference. , a series of methods for a class all return the this
object, allowing them to be joined into one single statement; each being called on the return value of the previous method.
While the methods used in method chaining may be atomic, a chain of method calls is inherently non-atomic, see Method chaining should not be used in a multithreaded environment because chained invocations of a set of methods are non-atomic and, consequently, noncompliant with CON07-J. Do not assume that a group of calls to independently atomic methods is atomic for details. Consequently methods that are involved in method chaining should not be invoked by multiple threads,
Noncompliant Code Example
Method chaining is a useful design pattern for building an object and setting its optional fields. A class that supports method chaining provides several setter methods that each return the this
reference. However, in a multithreaded environment, a thread may observe shared fields to contain inconsistent values. This noncompliant code example shows the Javabeans pattern which is not safe for multithreaded use.
...
Wiki Markup |
---|
This compliant solution uses the variant of the Builder pattern \[[Gamma 95|AA. Java References#Gamma 95]\] suggested by Bloch \[[Bloch 08|AA. Java References#Bloch 08]\] to ensure thread safety and atomicity of object creation. The method chaining is actually constrained to the {{USCurrency.Builder}} class which is only accessible by a single thread. |
Code Block | ||
---|---|---|
| ||
final class USCurrency { private final int quarters; private final int dimes; private final int nickels; private final int pennies; public USCurrency(Builder builder) { this.quarters = builder.quarters; this.dimes = builder.dimes; this.nickels = builder.nickels; this.pennies = builder.pennies; } // Static class member public static class Builder { private int quarters = 0; private int dimes = 0; private int nickels = 0; private int pennies = 0; public static Builder newInstance() { return new Builder(); } private Builder() {} // Setter methods public Builder setQuarters(int quantity) { this.quarters = quantity; return this; } public Builder setDimes(int quantity) { this.dimes = quantity; return this; } public Builder setNickels(int quantity) { this.nickels = quantity; return this; } public Builder setPennies(int quantity) { this.pennies = quantity; return this; } public USCurrency build() { return new USCurrency(this); } } } // Client code: private volatile USCurrency currency; // ... new Thread(new Runnable() { @Override public void run() { currency = USCurrency.Builder.newInstance().setQuarters(1).setDimes(1).build(); } }).start(); new Thread(new Runnable() { @Override public void run() { currency = USCurrency.Builder.newInstance().setQuarters(2).setDimes(2).build(); } }).start(); |
...
If input needs to be validated, ensure that the values are defensively copied prior to the validation (see FIO00-J. Defensively copy mutable inputs and mutable internal components for more information). The builder class does not violate SCP03-J. Do not expose sensitive private members of the outer class from within a nested class because it maintains a copy of the variables defined in the scope of the containing class. These take precedence and as a result, do not break encapsulation.
Exceptions
EX1: A class may use method chaining in a multithreaded environment if it sufficiently documents this fact. Client code must externally use some locking to ensure that the method calls are thread-safe.
Risk Assessment
Using method chaining in multithreaded environments without performing external locking can lead to non-deterministic behavior.
...