Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The "set only once" requirement feels a bit arbitrary. I'm fairly certain what you're looking for is a class that transitions permanently from uninitialized to initialized state. After all, it may be convenient to set an object's id more than once (via code reuse or whatever), as long as the id is not allowed to change after the object is "built".</p> <p>One fairly reasonable pattern is to keep track of this "built" state in a separate field:</p> <pre><code>public final class Example { private long id; private boolean isBuilt; public long getId() { return id; } public void setId(long id) { if (isBuilt) throw new IllegalArgumentException("already built"); this.id = id; } public void build() { isBuilt = true; } } </code></pre> <p>Usage:</p> <pre><code>Example e = new Example(); // do lots of stuff e.setId(12345L); e.build(); // at this point, e is immutable </code></pre> <p>With this pattern, you construct the object, set its values (as many times as is convenient), and then call <code>build()</code> to "immutify" it.</p> <p>There are several advantages to this pattern over your initial approach:</p> <ol> <li>There are no magic values used to represent uninitialized fields. For example, <code>0</code> is just as valid an id as any other <code>long</code> value.</li> <li>Setters have a consistent behavior. Before <code>build()</code> is called, they work. After <code>build()</code> is called, they throw, regardless of what values you pass. (Note the use of unchecked exceptions for convenience).</li> <li>The class is marked <code>final</code>, otherwise a developer could extend your class and override the setters.</li> </ol> <p>But this approach has a fairly big drawback: developers using this class can't know, <em>at compile time</em>, if a particular object has been initialized or not. Sure, you could add an <code>isBuilt()</code> method so developers can check, <em>at runtime</em>, if the object is initialized, but it would be so much more convenient to know this information at compile time. For that, you could use the builder pattern:</p> <pre><code>public final class Example { private final long id; public Example(long id) { this.id = id; } public long getId() { return id; } public static class Builder { private long id; public long getId() { return id; } public void setId(long id) { this.id = id; } public Example build() { return new Example(id); } } } </code></pre> <p>Usage:</p> <pre><code>Example.Builder builder = new Example.Builder(); builder.setId(12345L); Example e = builder.build(); </code></pre> <p>This is much better for several reasons:</p> <ol> <li>We're using <code>final</code> fields, so both the compiler and developers know these values cannot be changed.</li> <li>The distinction between initialized and uninitialized forms of the object is described via Java's type system. There is simply no setter to call on the object once it has been built.</li> <li>Instances of the built class are guaranteed thread safe.</li> </ol> <p>Yes, it's a bit more complicated to maintain, but IMHO the benefits outweigh the cost.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload