Enums, Exceptions, and Robust Error Handling

Enums and Domain States

ReadingPreview

You are viewing a free preview lesson.

Lesson Overview

Real applications-এ অনেক values একটি fixed set-এর মধ্যে সীমাবদ্ধ থাকে।

Examples:

Course status
Enrollment status
Payment status
Difficulty level
User role
Content type
Publication visibility

একটি course-এর status arbitrary text হওয়া উচিত নয়।

Valid values হতে পারে:

DRAFT
REVIEW
PUBLISHED
ARCHIVED

Weak design:

private String status;

এতে invalid values ঢুকতে পারে:

published
Publish
PUBLISHD
active
anything

Java-তে fixed, named, type-safe values model করার জন্য ব্যবহার করা হয়:

enum

এই lesson-এ আমরা শিখব:

  • enum কী
  • String constants-এর সমস্যা
  • Enum declare এবং use করা
  • Enum comparison
  • values() এবং valueOf()
  • switch with enum
  • Fields, constructors, এবং methods inside enum
  • Domain state transitions
  • External input safely parse করা
  • Enum নাম persistence/API-তে ব্যবহারের trade-off
  • কেন ordinal() persist করা উচিত নয়
  • Enum কখন ব্যবহার করা উচিত এবং কখন নয়

Learning Objectives

এই lesson শেষে আপনি পারবেন:

  • Fixed domain valuesকে enum দিয়ে model করতে
  • Invalid string state prevent করতে
  • Enum values compare করতে
  • Enum-এর ওপর switch লিখতে
  • Enum-এর মধ্যে fields এবং methods রাখতে
  • Valid state transitions enforce করতে
  • Unsafe valueOf() usage identify করতে
  • External text থেকে enum safely parse করতে
  • name(), toString(), এবং ordinal() distinguish করতে

The Problem with Strings

Suppose a course stores status as String:

public final class Course {

    private String status;

    public Course(
            String status
    ) {
        this.status = status;
    }
}

Caller can create:

new Course(
        "PUBLISHED"
);

But also:

new Course(
        "publish"
);
new Course(
        "PUBLISED"
);
new Course(
        ""
);

Compiler cannot understand which strings are valid।


String Comparison Is Fragile

if (
        course.getStatus()
                .equals(
                        "PUBLISHED"
                )
) {
}

Potential problems:

  • Typo
  • Case mismatch
  • Whitespace
  • Repeated literal values
  • Refactoring difficulty
  • No compile-time validation
  • Unsupported status accepted

String Constants Improve Only Part of the Problem

Possible improvement:

public final class CourseStatuses {

    public static final String DRAFT =
            "DRAFT";

    public static final String PUBLISHED =
            "PUBLISHED";

    private CourseStatuses() {
    }
}

Usage:

course.setStatus(
        CourseStatuses.PUBLISHED
);

But method still accepts any String:

course.setStatus(
        "BROKEN"
);

Constants reduce repeated literals।

They do not create a truly restricted type।


Define an Enum

public enum CourseStatus {

    DRAFT,
    REVIEW,
    PUBLISHED,
    ARCHIVED
}

Usage:

CourseStatus status =
        CourseStatus.DRAFT;

The variable can hold only valid CourseStatus values।

Invalid:

CourseStatus status =
        "DRAFT";

Compiler rejects it।

Invalid:

CourseStatus status =
        CourseStatus.ACTIVE;

Compiler rejects it because ACTIVE is not declared।


Enum Is a Type

CourseStatus

একটি real Java type।

Method parameter:

public void changeStatus(
        CourseStatus status
) {
}

Field:

private CourseStatus status;

Return type:

public CourseStatus getStatus() {
    return status;
}

Compiler now understands the valid domain।


A Course Using Enum State

public final class Course {

    private final long id;
    private final String title;

    private CourseStatus status;

    public Course(
            long id,
            String title
    ) {
        if (id <= 0) {
            throw new IllegalArgumentException(
                    "Course ID must be positive."
            );
        }

        if (
                title == null
                || title.isBlank()
        ) {
            throw new IllegalArgumentException(
                    "Course title is required."
            );
        }

        this.id = id;
        this.title = title.strip();

        this.status =
                CourseStatus.DRAFT;
    }

    public CourseStatus getStatus() {
        return status;
    }
}

Course always starts with:

CourseStatus.DRAFT

Caller arbitrary initial state inject করতে পারে না।


Comparing Enum Values

Enum values compare করতে == ব্যবহার করা safe এবং common।

if (
        status
        == CourseStatus.PUBLISHED
) {
}

Also works:

status.equals(
        CourseStatus.PUBLISHED
);

But == preferred because enum constants are unique instances।


Null-Safe Enum Comparison

Suppose:

CourseStatus status =
        null;

This is safe:

if (
        status
        == CourseStatus.PUBLISHED
) {
}

This can fail:

status.equals(
        CourseStatus.PUBLISHED
);

because status is null।

However, well-designed domain objects should usually reject null enum state।


Enums Can Be Used in switch

public static String describeStatus(
        CourseStatus status
) {
    if (status == null) {
        return "Unknown status";
    }

    return switch (status) {
        case DRAFT ->
                "The course is being prepared.";

        case REVIEW ->
                "The course is waiting for review.";

        case PUBLISHED ->
                "The course is available to learners.";

        case ARCHIVED ->
                "The course is no longer active.";
    };
}

Because every enum constant is covered, no default is required।


Why Exhaustive switch Is Useful

Suppose enum later adds:

SUSPENDED

An exhaustive switch expression may stop compiling until the new value is handled।

This helps prevent forgotten behavior।

A broad default can hide missing cases:

default ->
        "Other";

Use default only when fallback behavior is genuinely correct।


Traditional switch Statement

public static void printStatus(
        CourseStatus status
) {
    switch (status) {
        case DRAFT:
            System.out.println(
                    "Draft"
            );
            break;

        case REVIEW:
            System.out.println(
                    "In review"
            );
            break;

        case PUBLISHED:
            System.out.println(
                    "Published"
            );
            break;

        case ARCHIVED:
            System.out.println(
                    "Archived"
            );
            break;
    }
}

Modern switch expression is often more concise।


values()

Every enum automatically provides:

values()

Example:

CourseStatus[] statuses =
        CourseStatus.values();

Iterate:

for (
        CourseStatus status
        : CourseStatus.values()
) {
    System.out.println(
            status
    );
}

Output:

DRAFT
REVIEW
PUBLISHED
ARCHIVED

valueOf()

Every enum also provides:

valueOf(String name)

Example:

CourseStatus status =
        CourseStatus.valueOf(
                "PUBLISHED"
        );

Result:

CourseStatus.PUBLISHED

valueOf() Is Exact

This fails:

CourseStatus.valueOf(
        "published"
);

Because enum constant name is:

PUBLISHED

It is case-sensitive।

This also fails:

CourseStatus.valueOf(
        " PUBLISHED "
);

Whitespace is not ignored।

Failure:

IllegalArgumentException

Null input creates:

NullPointerException

Do Not Call valueOf() Directly on Untrusted Input

Weak:

CourseStatus status =
        CourseStatus.valueOf(
                requestStatus
        );

User input may be:

published
unknown
empty
null

The method can throw runtime exceptions।

External input should be normalized and validated deliberately।


Safe Parsing Method

public enum CourseStatus {

    DRAFT,
    REVIEW,
    PUBLISHED,
    ARCHIVED;

    public static CourseStatus fromText(
            String value
    ) {
        if (
                value == null
                || value.isBlank()
        ) {
            return null;
        }

        String normalizedValue =
                value.strip()
                        .toUpperCase();

        for (
                CourseStatus status
                : values()
        ) {
            if (
                    status.name()
                            .equals(
                                    normalizedValue
                            )
            ) {
                return status;
            }
        }

        return null;
    }
}

Usage:

CourseStatus status =
        CourseStatus.fromText(
                " published "
        );

Result:

CourseStatus.PUBLISHED

Unknown value returns:

null

Should Parsing Return Null?

Returning null is simple, but caller must check it।

Alternative designs:

Throw a custom exception
Return Optional<CourseStatus>
Return a Result<CourseStatus>
Return a default value

The correct choice depends on API requirements।

Do not silently map unknown input to a real status unless that fallback is valid।

Weak:

return DRAFT;

for every unknown input।

This may hide client bugs।


name()

CourseStatus.PUBLISHED.name()

returns:

PUBLISHED

name() is the exact constant identifier declared in code।

It cannot be overridden।

Useful for:

  • Stable technical representation
  • Logs
  • Database string storage
  • API values, if treated as a contract

toString()

By default:

CourseStatus.PUBLISHED.toString()

also returns:

PUBLISHED

But toString() can be overridden।

Therefore technical persistence should generally use:

name()

rather than a display-oriented toString()


Custom Display Label

public enum CourseStatus {

    DRAFT(
            "Draft"
    ),

    REVIEW(
            "In Review"
    ),

    PUBLISHED(
            "Published"
    ),

    ARCHIVED(
            "Archived"
    );

    private final String displayName;

    CourseStatus(
            String displayName
    ) {
        this.displayName =
                displayName;
    }

    public String getDisplayName() {
        return displayName;
    }
}

Usage:

CourseStatus.PUBLISHED
        .getDisplayName();

Result:

Published

Enum Constructors Are Not Public

Enum constructor:

CourseStatus(
        String displayName
)

is used only by declared constants।

Caller cannot do:

new CourseStatus(
        "Custom"
);

Enum instances are fixed by the declaration।


Enum with Multiple Fields

public enum Difficulty {

    BEGINNER(
            1,
            "Beginner"
    ),

    INTERMEDIATE(
            2,
            "Intermediate"
    ),

    ADVANCED(
            3,
            "Advanced"
    );

    private final int level;
    private final String displayName;

    Difficulty(
            int level,
            String displayName
    ) {
        this.level = level;
        this.displayName =
                displayName;
    }

    public int getLevel() {
        return level;
    }

    public String getDisplayName() {
        return displayName;
    }
}

Usage:

Difficulty.ADVANCED.getLevel()

returns:

3

Enum Can Have Behavior

public enum EnrollmentStatus {

    PENDING,
    ACTIVE,
    CANCELLED,
    COMPLETED;

    public boolean isActive() {
        return this
                == ACTIVE;
    }

    public boolean isTerminal() {
        return this
                == CANCELLED
                || this
                == COMPLETED;
    }
}

Usage:

if (
        enrollment.getStatus()
                .isTerminal()
) {
}

This is clearer than repeating:

status == CANCELLED
        || status == COMPLETED

throughout the application।


Behavior Belongs in the Enum When It Depends on the Value

Good enum behavior:

isTerminal()
canBeCancelled()
requiresPayment()

because these are directly determined by the enum constant।

Avoid putting unrelated service logic inside enum:

sendEmail()
saveToDatabase()
callPaymentGateway()

Enums should not become dependency-heavy service containers।


Domain State Transitions

Enums define valid states।

But enum alone does not define which transitions are allowed।

Possible course lifecycle:

DRAFT
  ↓
REVIEW
  ↓
PUBLISHED
  ↓
ARCHIVED

Not every transition should be valid।

Example invalid:

DRAFT → ARCHIVED
ARCHIVED → PUBLISHED
PUBLISHED → DRAFT

Domain class should enforce transition rules।


Weak Status Setter

public void setStatus(
        CourseStatus status
) {
    this.status = status;
}

Caller can perform any transition:

course.setStatus(
        CourseStatus.ARCHIVED
);

even if course is still a new draft।

A generic setter exposes state without protecting lifecycle।


Use Domain Operations

public boolean submitForReview() {
    if (
            status
            != CourseStatus.DRAFT
    ) {
        return false;
    }

    status =
            CourseStatus.REVIEW;

    return true;
}
public boolean publish() {
    if (
            status
            != CourseStatus.REVIEW
    ) {
        return false;
    }

    status =
            CourseStatus.PUBLISHED;

    return true;
}
public boolean archive() {
    if (
            status
            != CourseStatus.PUBLISHED
    ) {
        return false;
    }

    status =
            CourseStatus.ARCHIVED;

    return true;
}

Caller expresses intent:

course.submitForReview();
course.publish();
course.archive();

Not raw assignment।


A Complete Course Lifecycle

CourseStatus.java

package io.liveklass.course;

public enum CourseStatus {

    DRAFT(
            "Draft"
    ),

    REVIEW(
            "In Review"
    ),

    PUBLISHED(
            "Published"
    ),

    ARCHIVED(
            "Archived"
    );

    private final String displayName;

    CourseStatus(
            String displayName
    ) {
        this.displayName =
                displayName;
    }

    public String getDisplayName() {
        return displayName;
    }

    public boolean isPubliclyVisible() {
        return this
                == PUBLISHED;
    }

    public boolean isEditable() {
        return this
                == DRAFT
                || this
                == REVIEW;
    }

    public boolean isTerminal() {
        return this
                == ARCHIVED;
    }

    public static CourseStatus fromText(
            String value
    ) {
        if (
                value == null
                || value.isBlank()
        ) {
            return null;
        }

        String normalizedValue =
                value.strip()
                        .toUpperCase();

        for (
                CourseStatus status
                : values()
        ) {
            if (
                    status.name()
                            .equals(
                                    normalizedValue
                            )
            ) {
                return status;
            }
        }

        return null;
    }
}

Course.java

package io.liveklass.course;

public final class Course {

    private final long id;
    private final String title;

    private CourseStatus status;

    public Course(
            long id,
            String title
    ) {
        if (id <= 0) {
            throw new IllegalArgumentException(
                    "Course ID must be positive."
            );
        }

        if (
                title == null
                || title.isBlank()
        ) {
            throw new IllegalArgumentException(
                    "Course title is required."
            );
        }

        this.id = id;
        this.title = title.strip();

        this.status =
                CourseStatus.DRAFT;
    }

    public boolean submitForReview() {
        if (
                status
                != CourseStatus.DRAFT
        ) {
            return false;
        }

        status =
                CourseStatus.REVIEW;

        return true;
    }

    public boolean returnToDraft() {
        if (
                status
                != CourseStatus.REVIEW
        ) {
            return false;
        }

        status =
                CourseStatus.DRAFT;

        return true;
    }

    public boolean publish() {
        if (
                status
                != CourseStatus.REVIEW
        ) {
            return false;
        }

        status =
                CourseStatus.PUBLISHED;

        return true;
    }

    public boolean archive() {
        if (
                status
                != CourseStatus.PUBLISHED
        ) {
            return false;
        }

        status =
                CourseStatus.ARCHIVED;

        return true;
    }

    public long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public CourseStatus getStatus() {
        return status;
    }

    public boolean isEditable() {
        return status.isEditable();
    }

    public boolean isPubliclyVisible() {
        return status.isPubliclyVisible();
    }
}

Main.java

package io.liveklass;

import io.liveklass.course.Course;

public class Main {

    public static void main(
            String[] args
    ) {
        Course course =
                new Course(
                        1L,
                        "Java and OOP Foundation"
                );

        printState(
                course
        );

        System.out.println(
                "Publish directly: "
                + course.publish()
        );

        System.out.println(
                "Submit for review: "
                + course.submitForReview()
        );

        printState(
                course
        );

        System.out.println(
                "Publish: "
                + course.publish()
        );

        printState(
                course
        );

        System.out.println(
                "Archive: "
                + course.archive()
        );

        printState(
                course
        );
    }

    private static void printState(
            Course course
    ) {
        System.out.println();

        System.out.println(
                "Status: "
                + course.getStatus()
                        .getDisplayName()
        );

        System.out.println(
                "Editable: "
                + course.isEditable()
        );

        System.out.println(
                "Publicly visible: "
                + course.isPubliclyVisible()
        );
    }
}

Possible Output

Status: Draft
Editable: true
Publicly visible: false
Publish directly: false
Submit for review: true

Status: In Review
Editable: true
Publicly visible: false
Publish: true

Status: Published
Editable: false
Publicly visible: true
Archive: true

Status: Archived
Editable: false
Publicly visible: false

Why Keep Transition Logic in Course?

The enum knows properties of each state:

Editable?
Publicly visible?
Terminal?

The Course controls transitions:

Can this course move from current state to another?

This separation keeps domain ownership clear।


Alternative: Transition Logic Inside Enum

Possible:

public enum CourseStatus {

    DRAFT,
    REVIEW,
    PUBLISHED,
    ARCHIVED;

    public boolean canTransitionTo(
            CourseStatus target
    ) {
        if (target == null) {
            return false;
        }

        return switch (this) {
            case DRAFT ->
                    target == REVIEW;

            case REVIEW ->
                    target == DRAFT
                    || target == PUBLISHED;

            case PUBLISHED ->
                    target == ARCHIVED;

            case ARCHIVED ->
                    false;
        };
    }
}

Then course:

public boolean changeStatus(
        CourseStatus target
) {
    if (
            !status.canTransitionTo(
                    target
            )
    ) {
        return false;
    }

    status = target;

    return true;
}

This centralizes transition table।


Generic Transition Method Trade-Off

A generic method:

changeStatus(
        target
)

is flexible, but caller intent is less expressive।

Compare:

course.changeStatus(
        CourseStatus.PUBLISHED
);

with:

course.publish();

publish() communicates business action better এবং additional publication validation add করা সহজ।

Use generic transition method when:

  • Transition workflow truly generic
  • Caller chooses target state deliberately
  • Additional action-specific validation is minimal

Use named methods when domain actions matter।


Enum Constant-Specific Behavior

An enum constant can provide different behavior।

public enum DiscountType {

    NONE {
        @Override
        public long apply(
                long price
        ) {
            return price;
        }
    },

    TEN_PERCENT {
        @Override
        public long apply(
                long price
        ) {
            return price
                    - price
                    * 10
                    / 100;
        }
    },

    TWENTY_PERCENT {
        @Override
        public long apply(
                long price
        ) {
            return price
                    - price
                    * 20
                    / 100;
        }
    };

    public abstract long apply(
            long price
    );
}

Usage:

long finalPrice =
        DiscountType.TEN_PERCENT
                .apply(
                        100_000L
                );

Result:

90000

This uses polymorphic enum behavior।


When Constant-Specific Behavior Is Appropriate

Good when:

  • Fixed strategies are truly limited
  • Each enum value has a small behavior variation
  • No external dependencies required
  • New values should implement the behavior

Avoid when:

  • Strategies are dynamically configured
  • Many dependencies are needed
  • New strategies should be added without editing enum
  • Business rules are large and independently evolving

In those cases, interface-based strategy classes may be stronger।


Enum and Collections

Enums can be used in regular collections:

Set<UserRole> roles;
Map<CourseStatus, Integer> counts;
List<CourseStatus> history;

Java also provides specialized collections:

EnumSet
EnumMap

These are optimized for enum keys or values।

Example:

EnumSet<CourseStatus> editableStatuses =
        EnumSet.of(
                CourseStatus.DRAFT,
                CourseStatus.REVIEW
        );

Detailed EnumSet and EnumMap usage can be learned later।


ordinal()

Every enum constant has a position:

CourseStatus.DRAFT.ordinal()

Possible result:

0
CourseStatus.REVIEW.ordinal()

Possible result:

1

But ordinal is declaration position, not a stable domain ID।


Never Persist ordinal() as a Stable Status

Suppose initially:

DRAFT,
REVIEW,
PUBLISHED

Stored values:

DRAFT     → 0
REVIEW    → 1
PUBLISHED → 2

Later enum changes:

DRAFT,
SCHEDULED,
REVIEW,
PUBLISHED

Now:

REVIEW    → 2
PUBLISHED → 3

Old stored 2 may now be interpreted as REVIEW instead of PUBLISHED

This silently corrupts meaning।

Prefer stable strings:

status.name()

or an explicit stable code field।


Explicit Stable Code

public enum PaymentStatus {

    PENDING(
            "pending"
    ),

    PAID(
            "paid"
    ),

    FAILED(
            "failed"
    );

    private final String code;

    PaymentStatus(
            String code
    ) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

The code can remain stable even if enum declaration order changes।


API Contract Warning

If API returns:

PUBLISHED

from:

status.name()

then renaming enum constant:

PUBLISHED → LIVE

is a breaking API change।

Java refactoring tool may update internal code, but external clients still expect old value।

Treat serialized enum names as public contracts।


Database Storage Warning

Storing enum as text is generally safer than ordinal, but renaming still requires migration।

Example stored value:

PUBLISHED

Code changed to:

LIVE

Existing database rows no longer parse automatically।

Use deliberate migrations and stable names।


Enum vs Boolean

Weak:

private boolean published;

This supports only two states:

true
false

If domain later needs:

DRAFT
REVIEW
PUBLISHED
ARCHIVED

boolean becomes insufficient।

Use boolean when state is genuinely binary:

Email verified?
Feature enabled?
Terms accepted?

Use enum when values are multiple named alternatives।


Avoid Multiple Conflicting Booleans

Weak:

private boolean draft;
private boolean published;
private boolean archived;

Invalid combinations become possible:

draft = true
published = true
archived = true

One enum guarantees one current state:

private CourseStatus status;

Enum vs Class

Use enum when:

  • All valid instances are known and fixed
  • Values are named constants
  • New instances should not be created dynamically
  • Each value belongs to one closed type

Examples:

CourseStatus
Difficulty
UserRole
PaymentStatus
ContentType

Use a class when:

  • Values are created dynamically
  • Each instance has unique data
  • Number of values is unbounded
  • Runtime configuration determines instances

Examples:

Course
Learner
Coupon
Country
Currency amount

Do Not Use Enum for Frequently Changing Database Data

Suppose administrators can add new course categories without deployment।

Weak:

enum CourseCategory {
    PROGRAMMING,
    DESIGN,
    BUSINESS
}

Every new category requires code change and redeployment।

A database entity may be better।

Enums are strongest for code-level closed sets।


Enum Naming Conventions

Enum type:

CourseStatus
EnrollmentStatus
Difficulty

Constants:

DRAFT
IN_REVIEW
PUBLISHED

Use uppercase snake case for constants।

Avoid vague values:

FIRST
SECOND
OTHER

unless their meaning is genuinely clear।


Null Enum State

Weak:

private CourseStatus status;

without initialization।

State may remain null।

Better:

this.status =
        CourseStatus.DRAFT;

or validate constructor input:

if (status == null) {
    throw new IllegalArgumentException(
            "Course status is required."
    );
}

A domain state should usually always be valid।


Common Mistakes

Using Strings for a Closed Set

Compiler cannot restrict valid values।


Using Public Status Setter

Allows invalid lifecycle transitions।


Calling valueOf() on Untrusted Input

Unknown text throws runtime exception।


Comparing Enum Names as Strings

status.name()
        .equals(
                "PUBLISHED"
        )

Usually less safe than:

status
        == CourseStatus.PUBLISHED

Persisting ordinal()

Declaration reordering changes meaning।


Renaming Serialized Enum Constants Casually

Can break APIs and stored data।


Using Multiple Booleans for One State Machine

Creates contradictory combinations।


Putting Service Dependencies Inside Enum

Makes enum behavior heavy and difficult to test।


Using Enum for Runtime-Configurable Data

Requires deployment for every new value।


Returning a Default Status for Unknown Input

May hide invalid external data।


Practice Exercises

Exercise 1: Create EnrollmentStatus

Values:

PENDING
ACTIVE
CANCELLED
COMPLETED

Add methods:

isActive()
isTerminal()

Exercise 2: Replace Strings

Refactor:

private String difficulty;

Valid values:

BEGINNER
INTERMEDIATE
ADVANCED

Use an enum।


Exercise 3: Safe Parsing

Add:

static Difficulty fromText(
        String value
)

Requirements:

  • Ignore surrounding whitespace
  • Ignore case
  • Return null for unknown values

Exercise 4: Course Transitions

Implement:

DRAFT → REVIEW
REVIEW → DRAFT
REVIEW → PUBLISHED
PUBLISHED → ARCHIVED

Reject every other transition।


Exercise 5: Switch Expression

Create:

static String getLearnerMessage(
        EnrollmentStatus status
)

Return a different message for each status।


Exercise 6: Stable External Code

Create:

PaymentStatus

with explicit codes:

pending
paid
failed
refunded

Do not use ordinal()


Exercise 7: Choose Enum or Class

Choose which should be an enum:

  1. Course difficulty
  2. Learner
  3. Payment status
  4. Coupon
  5. User role
  6. Country managed by administrators
  7. Content type
  8. Course created by instructors

Explain each choice।


Predict the Result

Question 1

CourseStatus status =
        CourseStatus.PUBLISHED;

System.out.println(
        status
        == CourseStatus.PUBLISHED
);

Answer

true

Question 2

CourseStatus status =
        CourseStatus.valueOf(
                "published"
        );

Answer

Runtime-এ:

IllegalArgumentException

valueOf() case-sensitive।


Question 3

System.out.println(
        CourseStatus.PUBLISHED.name()
);

Answer

PUBLISHED

Question 4

for (
        CourseStatus status
        : CourseStatus.values()
) {
    System.out.println(
            status
    );
}

What does it print?

Answer

Every declared enum constant in declaration order।


Question 5

CourseStatus status =
        null;

System.out.println(
        status
        == CourseStatus.DRAFT
);

Answer

false

== comparison is null-safe।


Question 6

Course course =
        new Course(
                1L,
                "Java"
        );

System.out.println(
        course.publish()
);

Assume course can publish only from REVIEW

Answer

false

Initial state is DRAFT


Knowledge Check

Question 1

What problem does an enum solve?

Question 2

Why is String weak for fixed domain states?

Question 3

Can callers create new enum instances?

Question 4

How should enum constants usually be compared?

Question 5

What does values() return?

Question 6

What does valueOf() require?

Question 7

Why is direct valueOf() unsafe for external input?

Question 8

What is the difference between name() and custom display text?

Question 9

Why should ordinal() not be persisted?

Question 10

Why are named domain methods stronger than a generic status setter?

Question 11

When should behavior live inside an enum?

Question 12

When should enum not be used?

Question 13

Why is one enum better than multiple status booleans?

Question 14

Can enum constructors be called by application code?

Question 15

What must be considered before renaming a serialized enum constant?


Knowledge Check Answers

Answer 1

A fixed closed set of valid named valuesকে type-safeভাবে model করে।

Answer 2

Any arbitrary text accepted হয় এবং compiler valid values enforce করতে পারে না।

Answer 3

না।

Only declared enum constants exist।

Answer 4

Using ==

Answer 5

An array containing every declared constant।

Answer 6

The exact case-sensitive constant name।

Answer 7

Unknown, blank, differently cased, or null input exception তৈরি করতে পারে।

Answer 8

name() exact technical constant identifier; display text is user-facing and can differ।

Answer 9

Declaration order change হলে stored numeric meaning change হয়ে যেতে পারে।

Answer 10

They express business intent and enforce valid transitions।

Answer 11

When behavior is directly determined by the fixed enum value and remains small।

Answer 12

When instances are dynamic, administrator-configurable, or unbounded।

Answer 13

An enum guarantees exactly one valid current state and avoids conflicting boolean combinations।

Answer 14

না।

Constructors are used only by enum constant declarations।

Answer 15

API clients, database values, messages, and other external contracts may depend on the old name।


Lesson Summary

এই lesson-এ আমরা শিখেছি:

  • enum fixed named valuesকে type-safeভাবে model করে
  • String state arbitrary invalid values allow করে
  • String constants restricted type তৈরি করে না
  • Enum একটি real Java type
  • Enum constants unique instances
  • Enum comparison-এর জন্য == safe এবং common
  • Enum values switch-এ use করা যায়
  • Exhaustive switch নতুন values handle করতে সাহায্য করে
  • values() all constants return করে
  • valueOf() exact case-sensitive name require করে
  • External input direct valueOf()-এ দেওয়া unsafe
  • Safe parsing normalization এবং validation require করে
  • Enum fields, constructors, এবং methods রাখতে পারে
  • Display label technical constant name থেকে আলাদা হতে পারে
  • Enum state-specific behavior রাখতে পারে
  • Domain class valid state transitions enforce করতে পারে
  • Generic setter invalid lifecycle changes allow করতে পারে
  • Named methods business intent communicate করে
  • Constant-specific enum behavior limited strategies model করতে পারে
  • Enum values collections-এ ব্যবহার করা যায়
  • ordinal() stable domain identifier নয়
  • Serialized enum names external contracts হতে পারে
  • Boolean only genuine binary state-এর জন্য appropriate
  • Multiple status booleans contradictory state তৈরি করতে পারে
  • Enum closed code-level sets-এর জন্য strongest
  • Dynamic administrator-managed values সাধারণত enum হওয়া উচিত নয়
  • Null enum state avoid বা validate করা উচিত

Next Lesson

পরবর্তী lesson:

Introduction to Exceptions and Failure Handling

আমরা শিখব:

  • Normal flow vs exceptional flow
  • What an exception is
  • Exception hierarchy
  • Checked and unchecked exceptions
  • throw
  • throws
  • try, catch, and finally
  • Stack trace
  • Avoiding swallowed exceptions
  • When to return a result and when to throw
  • Building clear failure contracts