Modern Java

Modern Java Language Features

ReadingPreview

আপনি একটি free preview lesson দেখছেন।

Lesson Overview

Java language সময়ের সাথে অনেক evolve করেছে।

Modern Java-তে এমন কিছু language feature আছে যেগুলো repetitive syntax কমায় এবং intent আরও clearly express করতে সাহায্য করে।

Examples:

var
switch

as an expression।

"""
Text block
"""

এবং:

if (
        value instanceof Course course
) {
    ...
}

এই features-এর purpose হলো:

কম code লেখা

শুধু তা নয়।

Better goal:

কম ceremony
+
clearer intent
+
safer code

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

  • Local variable type inference with var
  • কখন var useful
  • কখন explicit type better
  • Modern switch expressions
  • Arrow-style switch
  • Returning values from switch
  • yield
  • Multiple case labels
  • Text blocks
  • Multiline text
  • Pattern matching with instanceof
  • Pattern matching with switch
  • Sealed type-এর high-level ধারণা
  • Modern syntax এবং readability
  • Modern features overuse না করা

Why Modern Language Features Matter

Consider:

Map<String, List<Course>> coursesByCategory =
        new HashMap<String, List<Course>>();

Modern Java-তে generic inference ব্যবহার করে আমরা already লিখতে পারি:

Map<String, List<Course>> coursesByCategory =
        new HashMap<>();

আর local variable-এর type initializer থেকে obvious হলে:

var coursesByCategory =
        new HashMap<String, List<Course>>();

লেখাও possible।

Modern Java-এর অনেক feature একই philosophy follow করে:

Compiler যেটা reliably infer করতে পারে,
সেটার unnecessary ceremony কমাও।

কিন্তু:

readability sacrifice করো না।

Local Variable Type Inference with var

Java-তে local variable declare করার সময়:

var

ব্যবহার করা যায়।

Example:

var title =
        "Java Foundation";

Compiler বুঝে:

title is String

var Is Not Dynamic Typing

এটি খুব important।

এই code:

var title =
        "Java";

এর পরে title permanently:

String

type-এর variable।

এটি valid:

title =
        "Backend";

এটি invalid:

title =
        42;

কারণ compiler already inferred:

String

Java Is Still Statically Typed

var মানে:

variable-এর type নেই

না।

বরং:

type compiler infer করেছে

So conceptually:

var title =
        "Java";

is equivalent in type meaning to:

String title =
        "Java";

var Works Only with Local Variables

Typical use:

void process() {
    var course =
            findCourse();
}

var local variables-এর জন্য।

এটি class field declaration-এর general replacement নয়।

Example:

class CourseService {

    var repository;
}

এভাবে field declare করা যায় না।


var Needs an Initializer

Compiler initializer দেখে type infer করে।

So this is invalid:

var course;

কারণ compiler জানে না type কী।

Need:

var course =
        new Course(
                "JAVA",
                "Java Foundation"
        );

var with Collections

Example:

var courses =
        new ArrayList<Course>();

Compiler infers:

ArrayList<Course>

Interface Type vs Concrete Type

Compare:

List<Course> courses =
        new ArrayList<>();

with:

var courses =
        new ArrayList<Course>();

First version communicates:

আমি variable-টিকে List abstraction হিসেবে ব্যবহার করছি।

Second version inferred type:

ArrayList<Course>

এটি একটি subtle design difference।


var Can Change the Visible Abstraction

Suppose:

var courses =
        new ArrayList<Course>();

Now reader sees concrete implementation।

Whereas:

List<Course> courses =
        new ArrayList<>();

communicates:

Only List behavior matters.

So var সবসময় shorter হলেও better নয়।


Good var Example

var course =
        repository.findByCode(
                "JAVA"
        ).orElseThrow();

যদি surrounding code থেকে type obvious হয়, var noise কমাতে পারে।


Another Good Example

var formatter =
        DateTimeFormatter.ofPattern(
                "dd MMM yyyy"
        );

Initializer থেকেই type clear।


Less Clear Example

var result =
        service.process(
                request
        );

result কী?

Course?
List<Course>?
boolean?
ProcessingResult?

Reader-কে method definition দেখতে হতে পারে।

Explicit type clearer:

EnrollmentResult result =
        service.process(
                request
        );

Practical Rule for var

Use var when:

initializer type obvious
variable name meaningful
type itself important information hide করছে না

Avoid when:

reader type বুঝতে পারছে না
generic API return type unclear
abstraction boundary important

var Should Reduce Noise, Not Information

Good:

var now =
        Instant.now();

Clear।

Potentially less clear:

var data =
        execute();

Both variable name এবং type ambiguous।

Problem var নয়।

Problem:

information loss

var with Diamond Operator

This can be problematic:

var values =
        new ArrayList<>();

Without additional type context, inferred generic type may not be what you intended।

Better:

var values =
        new ArrayList<String>();

or:

List<String> values =
        new ArrayList<>();

The second is often clearer।


var and Null

This is invalid:

var course =
        null;

Compiler cannot infer a useful type from only null


var and Method Return Types

Example:

var courses =
        loadCourses();

This compiles if method return type known।

But readability question remains:

Is return type obvious enough here?

Use judgment।


Traditional switch

Suppose:

enum CourseStatus {
    DRAFT,
    PUBLISHED,
    ARCHIVED
}

Traditional switch:

String label;

switch (
        status
) {
    case DRAFT:
        label =
                "Draft";
        break;

    case PUBLISHED:
        label =
                "Published";
        break;

    case ARCHIVED:
        label =
                "Archived";
        break;

    default:
        throw new IllegalStateException();
}

এখানে অনেক ceremony:

temporary variable
assignment
break

Modern Java switch expressions এটিকে cleaner করতে পারে।


Switch Expression

String label =
        switch (
                status
        ) {
            case DRAFT ->
                    "Draft";

            case PUBLISHED ->
                    "Published";

            case ARCHIVED ->
                    "Archived";
        };

এখন পুরো switch একটি value produce করছে।


Statement vs Expression

Traditional switch often used as:

statement

Modern switch can be:

expression

Meaning:

switch itself returns a value

Why This Is Useful

Instead of:

String result;

switch (...) {
    ...
}

we can directly write:

String result =
        switch (...) {
            ...
        };

This reduces mutable intermediate state।


Arrow-Style Cases

Modern syntax:

case DRAFT ->
        "Draft";

Arrow form-এর advantage:

no accidental fall-through
no break needed

Traditional Fall-Through Problem

Old switch:

switch (
        value
) {
    case 1:
        System.out.println(
                "One"
        );

    case 2:
        System.out.println(
                "Two"
        );
}

If value == 1, lack of break means execution may continue into case 2।

Arrow-style switch এই accidental fall-through avoid করে।


Multiple Labels in One Case

Suppose:

enum Role {
    ADMIN,
    INSTRUCTOR,
    LEARNER,
    SUPPORT
}

Group:

String area =
        switch (
                role
        ) {
            case ADMIN,
                 SUPPORT ->
                    "Operations";

            case INSTRUCTOR ->
                    "Teaching";

            case LEARNER ->
                    "Learning";
        };

Multiple labels comma দিয়ে combine করা যায়।


Switch with Multiple Statements

Suppose one case-এ multiple statements দরকার।

String message =
        switch (
                status
        ) {
            case DRAFT -> {
                System.out.println(
                        "Preparing draft message"
                );

                yield "Course is still a draft.";
            }

            case PUBLISHED ->
                    "Course is available.";

            case ARCHIVED ->
                    "Course is archived.";
        };

yield

Switch expression-এর block case থেকে result দিতে:

yield

ব্যবহার করা হয়।

Example:

case DRAFT -> {
    String message =
            buildMessage();

    yield message;
}

yield Is Not return

return current method থেকে বের হয়ে যায়।

yield current switch expression-এর value provide করে।

Example:

return switch (
        status
) {
    ...
};

Inside a block:

yield value;

switch-এর result।


Switch Expression and Exhaustiveness

যখন compiler জানে possible cases finite, যেমন enum, সব cases handle করলে default প্রয়োজন নাও হতে পারে।

Example:

switch (
        status
) {
    case DRAFT -> ...
    case PUBLISHED -> ...
    case ARCHIVED -> ...
}

এটি useful কারণ future-এ enum-এ নতুন value যোগ হলে compiler কিছু switch expressions update করার প্রয়োজন detect করতে পারে।


Avoid Unnecessary default for Closed Enum Logic

Suppose:

enum Status {
    ACTIVE,
    CANCELLED
}

If you write:

switch (
        status
) {
    case ACTIVE ->
            "Active";

    default ->
            "Other";
}

Future:

PAUSED

add হলে code silently:

Other

দেবে।

কিন্তু explicit cases:

switch (
        status
) {
    case ACTIVE ->
            "Active";

    case CANCELLED ->
            "Cancelled";
}

future change compiler-visible হতে পারে।

Closed domain states-এ exhaustive handling valuable।


Switch Is Not Always Better Than if

Suppose:

if (
        score >= 80
) {
    ...
}

A switch force করার দরকার নেই।

Use switch when logic naturally depends on:

discrete alternatives

Examples:

enum
known command types
sealed hierarchy
specific values

Text Blocks

Multiline String লেখার জন্য modern Java provides:

Text Blocks

Traditional:

String message =
        "Hello\n"
        + "Welcome to LiveKlass\n"
        + "Learn Java";

Text block:

String message =
        """
        Hello
        Welcome to LiveKlass
        Learn Java
        """;

Why Text Blocks Matter

Multiline content-এর ক্ষেত্রে traditional escaped Strings noisy হতে পারে।

Examples:

JSON
SQL
HTML
email templates
test fixtures
documentation text

Text blocks এগুলো readable করতে পারে।


JSON Example

Traditional:

String json =
        "{\n"
        + "  \"code\": \"JAVA\",\n"
        + "  \"title\": \"Java Foundation\"\n"
        + "}";

Text block:

String json =
        """
        {
          "code": "JAVA",
          "title": "Java Foundation"
        }
        """;

Much easier to read।


SQL Example

String query =
        """
        SELECT
            id,
            code,
            title
        FROM courses
        WHERE status = ?
        ORDER BY title
        """;

Even though this course does not teach database programming, text blocks-এর use case বোঝার জন্য এটি useful example।


HTML Example

String html =
        """
        <html>
            <body>
                <h1>Welcome</h1>
            </body>
        </html>
        """;

Text Blocks Are Still Strings

A text block has type:

String

So:

String message =
        """
        Hello
        World
        """;

is normal String value।

You can call:

message.length();

or:

message.contains(
        "Hello"
);

Text Blocks Do Not Mean Template Engine

Example:

"""
Hello {name}
"""

Java automatically {name} replace করবে না।

Text block শুধু multiline String syntax।

Dynamic values still need an explicit approach।

Example:

String message =
        """
        Hello %s
        Welcome to the course.
        """.formatted(
                name
        );

.formatted()

Example:

String message =
        """
        Course: %s
        Price: %d
        """.formatted(
                course.title(),
                course.priceInPaisa()
        );

This can be useful for simple formatting।


Avoid Building Complex Templates Manually

Large:

HTML
emails
documents

শুধু text block + string formatting দিয়ে maintain করা difficult হতে পারে।

Text blocks useful, কিন্তু proper template system-এর replacement নয়।


Pattern Matching with instanceof

Traditional Java:

if (
        value instanceof Course
) {
    Course course =
            (Course) value;

    System.out.println(
            course.title()
    );
}

Notice duplication:

check type
then cast same type

Modern pattern matching simplifies this।


Modern instanceof

if (
        value instanceof Course course
) {
    System.out.println(
            course.title()
    );
}

Here:

course

automatically typed as:

Course

inside the valid scope।


Mental Model

value instanceof Course course

means:

value কি Course?

যদি হয়,
তাহলে সেটিকে Course হিসেবে course নাম দিয়ে use করো।

No Manual Cast Needed

Old:

Course course =
        (Course) value;

Modern pattern matching avoids this duplicated cast।


Pattern Variable Scope

Example:

if (
        value instanceof Course course
) {
    System.out.println(
            course.title()
    );
}

course is usable where compiler knows the pattern matched।

Outside that valid scope, it may not exist।


Pattern Matching with Conditions

Example:

if (
        value instanceof Course course
        && course.published()
) {
    System.out.println(
            course.title()
    );
}

This works naturally।

Why?

Because right side of && runs only if:

value is Course

so course is known there।


Useful Guard-Style Code

Instead of:

if (
        !(value instanceof Course)
) {
    return;
}

Course course =
        (Course) value;

modern Java can express:

if (
        !(value instanceof Course course)
) {
    return;
}

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

Compiler tracks where the pattern variable is safely available।


Pattern Matching with switch

Modern switch can also match types।

Suppose:

interface Notification {
}

Implementations:

record EmailNotification(
        String email
) implements Notification {
}

record SmsNotification(
        String phone
) implements Notification {
}

Then conceptually:

String destination =
        switch (
                notification
        ) {
            case EmailNotification email ->
                    email.email();

            case SmsNotification sms ->
                    sms.phone();
        };

This lets switch branch based on runtime type while binding a typed variable।


Why Pattern Switch Can Be Useful

Without it:

if (
        notification instanceof EmailNotification email
) {
    ...
} else if (
        notification instanceof SmsNotification sms
) {
    ...
}

For a closed set of variants, switch can communicate:

handle each possible shape

more clearly।


Pattern Switch Is Not a Replacement for Polymorphism

Suppose each notification can send itself:

interface Notification {

    void send();
}

Then:

notification.send();

may be much better than:

switch (
        notification
) {
    case EmailNotification ...
    case SmsNotification ...
}

When Pattern Matching Fits Better

Pattern switch is useful when behavior:

belongs to the operation

rather than naturally to each subtype।

Example:

formatting for a report
external serialization
cross-cutting inspection
one-off transformation

If subtype-specific behavior belongs inside the type itself, polymorphism may be cleaner।


Sealed Types — High-Level Idea

Sometimes we want an inheritance hierarchy where only specific implementations are allowed।

Conceptually:

PaymentResult
├── PaymentSucceeded
├── PaymentFailed
└── PaymentPending

If the allowed variants are intentionally closed, Java supports:

sealed types

Example

sealed interface PaymentResult
        permits PaymentSucceeded,
                PaymentFailed {
}

Then:

record PaymentSucceeded(
        String paymentId
) implements PaymentResult {
}

and:

record PaymentFailed(
        String reason
) implements PaymentResult {
}

Why Sealed Types Are Useful

A sealed hierarchy communicates:

Only these known variants may represent this concept.

This can combine nicely with exhaustive pattern matching।

Example conceptually:

String message =
        switch (
                result
        ) {
            case PaymentSucceeded success ->
                    "Paid: "
                    + success.paymentId();

            case PaymentFailed failure ->
                    "Failed: "
                    + failure.reason();
        };

Closed Domain States

This works well for concepts like:

Command result
Payment result
Validation result
Message type
Workflow event variant

when valid implementations are intentionally limited।


Sealed Types Are Not Always Needed

Do not make every interface sealed।

If extension is expected:

plugins
third-party implementations
open framework contracts

a normal interface may be better।

Use sealed types when:

closed hierarchy

is part of the domain design।


Enum vs Sealed Hierarchy

If state needs only names:

enum CourseStatus {
    DRAFT,
    PUBLISHED,
    ARCHIVED
}

is excellent।

If each variant carries different data:

Success → paymentId
Failure → reason

sealed hierarchy + records may model the domain better।


Modern Switch with Enum

Example:

enum CourseStatus {
    DRAFT,
    PUBLISHED,
    ARCHIVED
}
String action =
        switch (
                status
        ) {
            case DRAFT ->
                    "Continue editing";

            case PUBLISHED ->
                    "View course";

            case ARCHIVED ->
                    "Restore unavailable";
        };

Very direct mapping।


Modern Switch with Complex Calculation

long discount =
        switch (
                learnerType
        ) {
            case STANDARD ->
                    0;

            case STUDENT ->
                    price / 10;

            case PREMIUM -> {
                long calculated =
                        price / 5;

                yield Math.min(
                        calculated,
                        100_000
                );
            }
        };

A block is useful when one branch needs multiple statements।


Avoid Huge Switch Branches

If each case contains:

30 lines
validation
network call
file handling
complex business rules

switch expression becomes hard to read।

Extract methods:

return switch (
        command.type()
) {
    case CREATE ->
            handleCreate(
                    command
            );

    case UPDATE ->
            handleUpdate(
                    command
            );

    case DELETE ->
            handleDelete(
                    command
            );
};

Modern Syntax Should Reveal Intent

Good modern Java:

var now =
        Instant.now();
String label =
        switch (
                status
        ) {
            case DRAFT ->
                    "Draft";

            case PUBLISHED ->
                    "Published";

            case ARCHIVED ->
                    "Archived";
        };
if (
        value instanceof Course course
) {
    ...
}

These reduce ceremony without hiding meaning।


Modern Syntax Can Also Hide Meaning

Consider:

var x =
        service.execute(
                a,
                b,
                c
        );

Reader has little idea:

what x is
what execute returns
what this operation means

The solution is not:

never use var

It is:

preserve useful information.

Explicit Type Can Be Documentation

Example:

Optional<Course> course =
        repository.findByCode(
                code
        );

The type communicates:

Course may be absent

Compare:

var course =
        repository.findByCode(
                code
        );

This removes that immediate signal।

Both compile।

First may be more educational/readable।


Domain Types Deserve Visibility

If a type communicates important meaning:

Money
CourseCode
EnrollmentResult
Optional<Course>
Duration
Instant

showing the type can improve readability।


var Is Great for Verbose Mechanical Types

Example:

var iterator =
        courses.iterator();

or:

var formatter =
        DateTimeFormatter.ofPattern(
                "dd MMM yyyy"
        );

The initializer tells the story।


Modern Java and Immutability

Many modern features work nicely with immutable style।

Example:

String label =
        switch (
                status
        ) {
            ...
        };

Instead of:

String label =
        null;

switch (...) {
    label = ...;
}

Less mutable intermediate state।


Modern Features and Records

We have already used:

record

in previous lessons।

Example:

record CourseSummary(
        String code,
        String title
) {
}

Records reduce boilerplate for value-oriented data models।

We will study records more directly in the next lesson।


Practical Example — Status Description

public class Main {

    public static void main(String[] args) {
        var status =
                CourseStatus.PUBLISHED;

        var message =
                description(
                        status
                );

        System.out.println(
                message
        );
    }

    static String description(
            CourseStatus status
    ) {
        return switch (
                status
        ) {
            case DRAFT ->
                    "Course is being prepared.";

            case PUBLISHED ->
                    "Course is available.";

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

    enum CourseStatus {
        DRAFT,
        PUBLISHED,
        ARCHIVED
    }
}

Practical Example — Pattern Matching

public class Main {

    public static void main(String[] args) {
        printLength(
                "Java"
        );

        printLength(
                42
        );
    }

    static void printLength(
            Object value
    ) {
        if (
                value instanceof String text
        ) {
            System.out.println(
                    text.length()
            );
        }
    }
}

No explicit cast required।


Practical Example — Text Block

public class Main {

    public static void main(String[] args) {
        String course =
                "Java Foundation";

        String message =
                """
                Welcome to LiveKlass

                Course: %s

                Learn consistently.
                Build deliberately.
                """.formatted(
                        course
                );

        System.out.println(
                message
        );
    }
}

Practical Example — Sealed Result

public class Main {

    public static void main(String[] args) {
        PaymentResult result =
                new PaymentSucceeded(
                        "PAY-1001"
                );

        String message =
                describe(
                        result
                );

        System.out.println(
                message
        );
    }

    static String describe(
            PaymentResult result
    ) {
        return switch (
                result
        ) {
            case PaymentSucceeded success ->
                    "Payment succeeded: "
                    + success.paymentId();

            case PaymentFailed failure ->
                    "Payment failed: "
                    + failure.reason();
        };
    }

    sealed interface PaymentResult
            permits PaymentSucceeded,
                    PaymentFailed {
    }

    record PaymentSucceeded(
            String paymentId
    ) implements PaymentResult {
    }

    record PaymentFailed(
            String reason
    ) implements PaymentResult {
    }
}

Common Mistake 1 — Thinking var Means Dynamic Type

Wrong mental model:

আজ String
কাল Integer

Java var does not work like that।

Type compile time-এই fixed।


Common Mistake 2 — Using var Everywhere

This:

var a =
        doSomething();

var b =
        process(
                a
        );

var c =
        transform(
                b
        );

may make type flow difficult to understand।

Use explicit types where they provide important context।


Common Mistake 3 — Unclear Variable Names with var

Bad combination:

var x =
        load();

Much better:

var publishedCourses =
        loadPublishedCourses();

or explicit:

List<Course> courses =
        loadPublishedCourses();

Common Mistake 4 — Assuming Switch Expression Needs break

Arrow-style:

case DRAFT ->
        "Draft";

does not use:

break

Common Mistake 5 — Using return Instead of yield

Inside a multi-statement switch-expression case:

case DRAFT -> {
    ...
    yield value;
}

yield provides switch result।

return would return from the enclosing method।


Common Mistake 6 — Adding default Without Thinking

For a closed enum, a broad:

default

can hide newly added enum constants।

Explicit exhaustive cases can make future changes more visible।


Common Mistake 7 — Using Switch for Complex Business Workflows

A switch with huge cases may indicate responsibilities should be moved to:

methods
strategies
polymorphic objects
services

depending on design।


Common Mistake 8 — Thinking Text Blocks Are Templates

Text blocks only make multiline String syntax easier।

They do not automatically provide:

escaping policy
HTML templating
conditional rendering
template variables

Common Mistake 9 — Using Pattern Matching Instead of Polymorphism Everywhere

If every subtype already knows how to perform an operation:

notification.send();

may be better than external type switching।

Pattern matching is useful, but not a replacement for good object design।


Common Mistake 10 — Sealing Every Interface

Sealed types are appropriate only when a hierarchy is intentionally closed।

Open extension points should remain open when required।


Practice 1 — Infer the Type

var name =
        "Sakib";

What is the type?

Answer

String

Practice 2

var count =
        10;

Type?

Answer

int

Practice 3 — Valid or Invalid?

var value;

Answer

Invalid।

Initializer না থাকায় type infer করা যায় না।


Practice 4

var value =
        null;

Answer

Invalid।

Useful type infer করার মতো initializer নেই।


Practice 5 — Choose var or Explicit Type

repository.findByCode(
        code
)

returns Optional<Course>

Which may communicate more information?

Answer

Often:

Optional<Course> course =
        repository.findByCode(
                code
        );

because absence semantics immediately visible।

var-ও valid, but may hide useful type information।


Practice 6 — Switch Expression

Convert:

CourseStatus.PUBLISHED

to:

"Published"

using switch।

Solution

String label =
        switch (
                status
        ) {
            case DRAFT ->
                    "Draft";

            case PUBLISHED ->
                    "Published";

            case ARCHIVED ->
                    "Archived";
        };

Practice 7 — Multiple Case Labels

Suppose:

enum Level {
    BEGINNER,
    INTERMEDIATE,
    ADVANCED
}

Beginner and Intermediate should return:

"Core"

Advanced:

"Specialized"

Solution

String category =
        switch (
                level
        ) {
            case BEGINNER,
                 INTERMEDIATE ->
                    "Core";

            case ADVANCED ->
                    "Specialized";
        };

Practice 8 — yield

When do we need yield?

Answer

যখন switch expression-এর একটি arrow case block:

case X -> {
    ...
}

multiple statements execute করে এবং শেষে একটি value provide করতে হয়।


Practice 9 — Text Block

Create multiline:

Hello
Java

Solution

String value =
        """
        Hello
        Java
        """;

Practice 10 — Pattern Matching

Convert:

if (
        value instanceof String
) {
    String text =
            (String) value;
}

Solution

if (
        value instanceof String text
) {
    ...
}

Practice 11 — Pattern with Condition

Check value is non-empty String।

Solution

if (
        value instanceof String text
        && !text.isBlank()
) {
    ...
}

Practice 12 — Enum or Sealed Hierarchy?

Need:

DRAFT
PUBLISHED
ARCHIVED

with no different payload per state।

Answer

Usually:

enum

is simpler।


Practice 13

Need:

Success contains paymentId
Failure contains reason

Answer

A sealed hierarchy with different record variants can be a good fit।


Practice 14 — Pattern Matching or Polymorphism?

Every Notification subtype already knows how to send itself।

Best starting point?

Answer

Prefer:

notification.send();

through polymorphism rather than switching on subtype externally।


Practice 15 — Modern Syntax Goal

What is the main reason to use modern Java features?

Answer

Not simply fewer characters।

The goal is:

reduce unnecessary ceremony
while preserving or improving readability and correctness

True or False

  1. var makes Java dynamically typed.
  2. var variables still have a compile-time type.
  3. var can be used without an initializer.
  4. var should always replace explicit local types.
  5. Switch expressions can produce a value.
  6. Arrow-style switch cases require break.
  7. yield can provide a value from a switch-expression block.
  8. Text blocks are still String values.
  9. Text blocks automatically behave as template engines.
  10. Pattern matching with instanceof can eliminate repeated casts.
  11. Pattern matching should replace all polymorphism.
  12. Sealed types can model intentionally closed hierarchies.
  13. Enums are still useful in modern Java.
  14. A broad default case can sometimes hide newly added enum states.
  15. Modern syntax should be chosen primarily for readability.

Answers

1. False
2. True
3. False
4. False
5. True
6. False
7. True
8. True
9. False
10. True
11. False
12. True
13. True
14. True
15. True

Knowledge Check

Question 1

var আসলে কী করে?

Question 2

কেন var dynamic typing নয়?

Question 3

কখন explicit type var-এর চেয়ে clearer হতে পারে?

Question 4

Switch expression traditional switch থেকে কীভাবে আলাদা?

Question 5

Arrow-style switch-এর advantage কী?

Question 6

yield কী কাজ করে?

Question 7

Text block কেন useful?

Question 8

Pattern matching with instanceof কী boilerplate remove করে?

Question 9

Pattern matching এবং polymorphism-এর relationship কী?

Question 10

Sealed type কী ধরনের hierarchy model করতে useful?

Question 11

Enum এবং sealed hierarchy-এর মধ্যে practical difference কী?

Question 12

Modern language features ব্যবহার করার সবচেয়ে important principle কী?


Knowledge Check Answers

Answer 1

var initializer দেখে local variable-এর compile-time type infer করে।

Example:

var title =
        "Java";

Compiler infer করে:

String

Answer 2

কারণ type inference-এর পরে variable-এর type fixed থাকে।

var value =
        "Java";

এর পরে:

value =
        10;

valid নয়।

Answer 3

যখন type নিজেই important information communicate করে।

Examples:

Optional<Course>
Instant
Duration
EnrollmentResult

অথবা initializer থেকে return type obvious নয়।

Answer 4

Modern switch একটি expression হিসেবে value produce করতে পারে।

Example:

String label =
        switch (
                status
        ) {
            ...
        };

Traditional switch সাধারণত control-flow statement হিসেবে বেশি ব্যবহৃত হতো।

Answer 5

Arrow-style cases accidental fall-through avoid করে এবং break boilerplate প্রয়োজন হয় না।

Answer 6

একটি multi-statement switch-expression case block থেকে switch-এর result value provide করে।

Answer 7

Multiline Strings যেমন JSON, SQL, HTML বা long messages readableভাবে লিখতে সাহায্য করে।

Answer 8

Repeated:

instanceof

check-এর পরে explicit cast:

(Type) value

লিখতে হয় না।

Answer 9

Pattern matching subtype inspect করার concise tool।

কিন্তু behavior naturally subtype-এর responsibility হলে polymorphic method call often better।

Pattern matching polymorphism-এর replacement নয়।

Answer 10

যেখানে valid subtypes intentionally limited এবং known।

Example:

Success
Failure

style result hierarchy।

Answer 11

enum ভালো যখন variants মূলত fixed named states।

Sealed hierarchy useful যখন প্রতিটি variant আলাদা type এবং আলাদা data carry করতে পারে।

Answer 12

Feature modern বা shorter বলে ব্যবহার করা নয়।

Use it when it:

reduces ceremony
clarifies intent
preserves type meaning
improves maintainability

Practical Modern Java Guide

Use var when:

initializer clearly reveals type
explicit type adds little information

Use explicit type when:

type communicates domain meaning
return type is not obvious
abstraction matters

Use switch expression when:

one input state maps to one result

Use text block when:

multiline String readability matters

Use instanceof pattern matching when:

type check followed by cast is required

Use sealed hierarchy when:

allowed subtypes are intentionally closed

Use polymorphism when:

behavior naturally belongs to each subtype

Core Mental Model

Modern Java features-এর উদ্দেশ্য:

Code clever করা নয়।

Unnecessary ceremony কমানো।

For example:

Old:

if (
        value instanceof Course
) {
    Course course =
            (Course) value;

    ...
}

Modern:

if (
        value instanceof Course course
) {
    ...
}

Old:

String label;

switch (
        status
) {
    case DRAFT:
        label =
                "Draft";
        break;

    ...
}

Modern:

String label =
        switch (
                status
        ) {
            case DRAFT ->
                    "Draft";

            ...
        };

Important principle:

Remove repetition,
not meaning.

Lesson Summary

এই lesson-এ আমরা Modern Java-এর কিছু গুরুত্বপূর্ণ language features শিখেছি।

আমরা শিখেছি:

  • var local variable type inference support করে
  • var Java-কে dynamically typed করে না
  • Compiler initializer থেকে actual static type determine করে
  • var initializer ছাড়া ব্যবহার করা যায় না
  • var সব জায়গায় explicit type replace করা উচিত নয়
  • Important domain type visible রাখা readability improve করতে পারে
  • Switch expression directly value produce করতে পারে
  • Arrow-style switch accidental fall-through avoid করে
  • Multiple case labels combine করা যায়
  • Multi-statement switch case থেকে yield দিয়ে value দেওয়া যায়
  • Exhaustive enum handling future changes detect করতে সাহায্য করতে পারে
  • Text blocks multiline Strings readable করে
  • Text blocks normal String, template engine নয়
  • instanceof pattern matching type check এবং cast combine করে
  • Pattern variable safe scope-এ directly ব্যবহার করা যায়
  • Pattern matching with switch closed variants handle করতে useful
  • Pattern matching polymorphism replace করে না
  • Sealed types intentionally closed inheritance hierarchy model করতে পারে
  • Enum simple fixed states-এর জন্য এখনো excellent choice
  • Sealed hierarchy variants different data carry করলে useful হতে পারে
  • Modern Java syntax use করার goal হলো clarity, শুধু short code নয়

সবচেয়ে important rule:

Modern Java ব্যবহার করুন
যখন language feature
intent clearer করে।

শুধু modern দেখানোর জন্য নয়।

Next Lesson

পরবর্তী lesson:

Records and Modern Data Models

আমরা শিখব:

  • record কী
  • Record components
  • Generated constructor
  • Accessor methods
  • equals()
  • hashCode()
  • toString()
  • Compact constructors
  • Validation
  • Defensive copying
  • Immutable data modeling
  • Records এবং value objects
  • Record vs normal class
  • কখন Record appropriate
  • কখন Record inappropriate
  • Domain entities এবং value objects-এর difference