Method chaining is a convenient mechanism that allows multiple method invocations on the same object to occur in a single statement. A method-chaining implementation consists of a series of methods that return the this
reference. This implementation allows a caller to invoke methods in a chain by performing the next method invocation on the return value of the previous method in the chain.
While Although the methods used in method chaining can be atomic, the chain they comprise is inherently nonatomic. Consequently, callers of methods that are involved in method chaining must provide sufficient locking to guarantee that the entire chain of invocations is atomic, as shown in rule VNA03-J. Do not assume that a group of calls to independently atomic methods is atomic.
...
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, if accessed concurrently, a thread may observe shared fields to contain inconsistent values. This noncompliant code example shows the JavaBeans pattern, which is not thread-safe.:
Code Block | ||
---|---|---|
| ||
final class USCurrency { // Change requested, denomination (optional fields) private int quarters = 0; private int dimes = 0; private int nickels = 0; private int pennies = 0; public USCurrency() {} // Setter methods public USCurrency setQuarters(int quantity) { quarters = quantity; return this; } public USCurrency setDimes(int quantity) { dimes = quantity; return this; } public USCurrency setNickels(int quantity) { nickels = quantity; return this; } public USCurrency setPennies(int quantity) { pennies = quantity; return this; } } // Client code: class ExampleClientCode { private final USCurrency currency = new USCurrency(); // ... public ExampleClientCode() { Thread t1 = new Thread(new Runnable() { @Override public void run() { currency.setQuarters(1).setDimes(1); } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { currency.setQuarters(2).setDimes(2); } }); t2.start(); //... } } |
...
This compliant solution uses the variant of the Builder pattern [Gamma 1995], suggested by Bloch [Bloch 2008], to ensure the thread-safety and atomicity of object creation.
...
The Builder.newInstance()
factory method is called with any required arguments to obtain a Builder
instance. The optional parameters are set using the setter methods of the builder. The object construction concludes with the invocation of the build()
method. This pattern makes the USCurrency
class immutable and consequently thread-safe.
Note that the currency
field cannot be declared final because it is assigned a new immutable object. It is, however, declared volatile in compliance with rule VNA01-J. Ensure visibility of shared references to immutable objects.
When input must be validated, ensure that the values are defensively copied prior to validation . (See rule see OBJ06-J. Defensively copy mutable inputs and mutable internal components for more information). ) The Builder
class also complies with rule OBJ08-J. Do not expose private members of an outer class from within a nested class because it maintains a copy of the variables defined in the scope of the containing class. The private members within the nested class take precedence and, as a result, maintain encapsulation.
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
VNA04-J | lowLow | probableProbable | mediumMedium | P4 | L3 |
Bibliography
| |
Item 2, "Consider a builder when faced with many constructor parametersBuilder When Faced with Many Constructor Parameters" |
...