iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Constructors

Java constructors: default, parameterised, this(), super(), validation. The shape that protects invariants.

Java — constructors

EXAMPLE
// ===== Default =====
public class User {
    public User() {}    // no-arg constructor (compiler generates if you write none)
}

// ===== Parameterised =====
public class User {
    private final int id;
    private final String name;
    private String email;

    public User(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
}

// ===== Validation in constructor =====
public User(int id, String name, String email) {
    if (id <= 0) throw new IllegalArgumentException("id must be positive");
    if (name == null || name.isBlank()) throw new IllegalArgumentException("name required");
    if (email == null || !email.contains("@")) throw new IllegalArgumentException("invalid email");
    this.id = id;
    this.name = name;
    this.email = email;
}

// ===== Constructor chaining (this()) =====
public class User {
    private final int id;
    private final String name;
    private final String email;

    public User(int id, String name, String email) {
        this.id = id; this.name = name; this.email = email;
    }
    public User(int id, String name) {
        this(id, name, name + "@example.com");      // delegate to the main constructor
    }
    public User(int id) {
        this(id, "unknown");
    }
}
// this(...) MUST be the first statement.

// ===== Constructor chaining (super()) =====
public class Employee extends User {
    private final long salaryCents;

    public Employee(int id, String name, String email, long salaryCents) {
        super(id, name, email);                       // call parent constructor
        this.salaryCents = salaryCents;
    }
}

// If you don't call super(...), Java inserts super() with no args.
// If the parent has no no-arg constructor, you MUST call super(...) explicitly.

// ===== Static factory methods (recommended for many cases) =====
public class Order {
    private final UUID id;
    private final String customer;

    private Order(UUID id, String customer) {
        this.id = id; this.customer = customer;
    }
    public static Order create(String customer) {
        if (customer == null) throw new IllegalArgumentException("customer required");
        return new Order(UUID.randomUUID(), customer);
    }
    public static Order fromRow(int id, String customer) {
        return new Order(UUID.fromString(String.valueOf(id)), customer);
    }
}
// Pros: named, can return cached instances, can return subtype.

// ===== Records: constructors come free =====
public record User2(int id, String name, String email) {
    // Compact constructor for validation:
    public User2 {
        if (id <= 0) throw new IllegalArgumentException("id must be positive");
        if (email == null || !email.contains("@")) throw new IllegalArgumentException("bad email");
    }
}

// ===== Builder pattern (for many fields) =====
public class Request {
    private final String url;
    private final Map<String, String> headers;
    private final Duration timeout;

    private Request(Builder b) {
        this.url = b.url; this.headers = b.headers; this.timeout = b.timeout;
    }
    public static class Builder {
        private String url;
        private Map<String, String> headers = Map.of();
        private Duration timeout = Duration.ofSeconds(30);

        public Builder url(String u) { this.url = u; return this; }
        public Builder header(String k, String v) {
            this.headers = new HashMap<>(this.headers); this.headers.put(k, v); return this;
        }
        public Builder timeout(Duration t) { this.timeout = t; return this; }
        public Request build() {
            Objects.requireNonNull(url, "url");
            return new Request(this);
        }
    }
}

// Use:
Request r = new Request.Builder().url("...").timeout(Duration.ofSeconds(5)).build();

// ===== Patterns to internalise =====
// - final fields + validation in constructor = invariants from day 1
// - Static factory methods over public constructors when you need names / caching
// - records for value-shape classes with compact validation
// - Builder pattern for many optional fields

// ===== Pitfalls =====
// - Calling overridable methods from a constructor -> subclass sees uninitialised state
// - Leaking 'this' from constructor (e.g. registering with an EventBus) -> bad partial init
// - Forgetting super() when parent has no no-arg constructor
// - Throwing checked exceptions from a constructor — possible but awkward

Why it matters

Constructors guard invariants. Validate at construction, mark fields final, chain with this()/super(), reach for static factory methods or records when you need names or value shapes. The Builder pattern shines when there are many optional parameters; the goal is "you cannot make an invalid object".

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
public class Box {
    int w, h;
    public Box() { this(1, 1); }              // no-arg
    public Box(int w, int h) { this.w = w; this.h = h; }
}
Try it Yourself »

Discussion

Loading…