These statements are typically used in a "mutable-like" fashion:
bound statement: create an instance, then set the variables
batch statement: create an instance, then add the children
Currently the implementations are actually mutable, and therefore not thread-safe – and error-prone if you reuse them for async executions. It was done this way to avoid the cost of creating intermediary objects, but maybe we're overestimating that cost.
Benchmark the overhead of purely immutable approaches:
have each mutating method create a new instance
alternatively, use a builder like SimpleStatement. This has the advantage of only creating one intermediary object. On the other hand, this might be less convenient for custom implementations, because each needs its own builder (instead of having the whole contract captured in a single interface).
If we go with solution 1, we might want to use it for SimpleStatement too, for the sake of uniformity.
Also add a method to bind all the values in a bound statement in one go, like bind(Object...) in 3.x.