সরাসরি পর্বে যান
পর্ব ১০ · ৫৯-এর মধ্যে · মডিউল ২

সিদ্ধান্ত — if/else আর switch expression

Decisions in Java — if/else, Guard Clauses, Classic switch and Modern switch Expressions

Taught in Bangla, this lesson covers decisions in Java: if, else if and else with mandatory braces, why conditions must be boolean, ordering bugs in else-if chains, guard clauses and the ternary operator, the classic switch with break and fall-through, and modern switch expressions with arrow labels, yield, exhaustiveness and case null.

এই পর্বে যা শিখবেন

  • if / else if / else কীভাবে চলে, আর কেন Java শুধু boolean শর্ত মানে
  • else-if সারির ক্রমের বাগ ধরা, আর nested if-কে guard clause দিয়ে সোজা করা
  • পুরোনো switch-এর case, break আর fall-through — কোথায় কাজে লাগে, কোথায় বিপদ
  • আধুনিক switch expression: arrow label, yield, exhaustiveness আর String-এর তুলনা
  • case null, pattern matching-এর এক ঝলক, আর AI-এর লেখা সিদ্ধান্ত-কোড যাচাই

১ · প্রোগ্রাম যখন পথ বেছে নেয়

এখন পর্যন্ত আমাদের প্রোগ্রাম চলেছে সোজা রাস্তায় — ওপরের লাইন থেকে নিচের লাইন, একটার পর একটা। পর্ব ০৯-এ Scanner দিয়ে ইনপুট নিয়েছেন। কিন্তু ইনপুট নিয়ে লাভ কী, যদি প্রোগ্রাম সবার জন্য একই কাজ করে? নম্বর ৮৫ হলে “A+” আর ২০ হলে “F” — এই পার্থক্য করতে প্রোগ্রামকে সিদ্ধান্ত নিতে হয়।

ঢাকার একটি মোড়ে ট্রাফিক পুলিশের কথা ভাবুন। প্রতিটি গাড়ি এলে তিনি একটি প্রশ্নের হ্যাঁ/না উত্তর দেখেন: “এই রাস্তার সিগন্যাল কি সবুজ?” হ্যাঁ হলে গাড়ি যাবে, না হলে থামবে। তাঁর মাথায় “হয়তো” বা “৩ ভাগ সবুজ” বলে কিছু নেই — উত্তর ঠিক দুটির একটি। প্রোগ্রামের সিদ্ধান্তও এমন। প্রতিটি মোড়ে একটি শর্ত, যার উত্তর true অথবা false, আর সেই উত্তর দেখে প্রোগ্রাম রাস্তা বেছে নেয়।

  1. মান আসেmarks = 72ইনপুট বা হিসাব থেকে
  2. শর্ত যাচাইmarks >= 33ফল: true বা false
  3. একটি পথ চলে"পাস"অন্য পথটি বাদ
প্রতিটি সিদ্ধান্তের তিন ধাপ: মান, শর্ত, পথ। শর্তের ফল সবসময় ঠিক দুটির একটি।

Java-তে সিদ্ধান্ত নেওয়ার দুটি প্রধান হাতিয়ার আছে, আর এই পর্ব দুটিকে ঘিরেই:

🔀 if / else

যেকোনো boolean শর্ত — পরিসর (marks >= 80), একাধিক শর্ত (age < 12 || age >= 65), যেকোনো তুলনা।

নমনীয়, সব জায়গায় চলে

🎛️ switch

একটি মান নির্দিষ্ট কয়েকটি সম্ভাবনার কোনটি — দিন ১ থেকে ৭, কমান্ড "add"/"list", যাত্রীর ধরন।

অনেক পথের পরিষ্কার মানচিত্র
মূল ধারণা

সিদ্ধান্তের হৃদয় হলো একটি boolean মান। শর্ত লিখতে লাগে তুলনা (>=, ==) আর যুক্তি (&&, ||, !) — এগুলো পর্ব ০৭-এ শিখেছেন। এই পর্বে আমরা দেখব সেই boolean দিয়ে প্রোগ্রামের পথ কীভাবে বদলানো হয়, আর কোন ভুলগুলো সবচেয়ে বেশি হয়।

২ · if, else if, else — আর এক লাইনেও বন্ধনী

if statement-এর তিনটি রূপ আছে। প্রথমটি সবচেয়ে সরল — শর্ত সত্য হলে কিছু করো, নইলে কিছুই না:

if (balance < 100) {
    System.out.println("সতর্কতা: ব্যালেন্স কম");
}

দুই পথের একটি বেছে নিতে else, আর অনেক পথের জন্য else if দিয়ে সারি বানানো হয়:

if (temp >= 35) {
    System.out.println("প্রচণ্ড গরম — পানি খান");
} else if (temp >= 25) {
    System.out.println("আরামদায়ক");
} else {
    System.out.println("শীত পড়েছে — চাদর নিন");
}

খেয়াল করুন, else if আলাদা কোনো keyword নয়। এটি আসলে একটি else, যার ভেতরে আরেকটি if বসানো — শুধু সাজিয়ে লেখা হয় বলে একটি সারির মতো দেখায়। তাই সারির শেষ else বাধ্যতামূলক নয়; তবে “বাকি সব ক্ষেত্রে” কী হবে তা লিখে রাখা প্রায় সবসময় ভালো।

বন্ধনী ছাড়া if — একটি বিপজ্জনক সুবিধা

Java (C-র মতোই) এক লাইনের if-এ বন্ধনী ছাড়তে দেয়: if (x > 3) System.out.println("big");. নিয়মটি হলো — বন্ধনী না থাকলে if-এর অধীনে থাকে শুধু পরের একটি statement. সমস্যা শুরু হয় যখন কেউ পরে আরেকটি লাইন যোগ করেন, আর indentation দেখে ভাবেন সেটিও if-এর ভেতরে:

int x = 5;
if (x > 3)
    System.out.println("big");
    System.out.println("always");   // দেখতে if-এর ভেতরে, আসলে বাইরে

Compiler-এর কাছে indentation-এর কোনো মূল্য নেই (পর্ব ০৪)। "always" লাইনটি সবসময় চলে — x যা-ই হোক। এখানে x = 5 বলে আউটপুটে দুটোই আসে, তাই বাগটি টেস্টেও লুকিয়ে থাকে; x = 1 দিলে ধরা পড়ত।

“goto fail” — একটি লাইনের দাম। ২০১৪ সালের ফেব্রুয়ারিতে Apple তাদের iOS আর macOS-এর একটি গুরুতর নিরাপত্তা ত্রুটি ঠিক করে (CVE-2014-1266). SSL/TLS সংযোগ যাচাইয়ের C কোডে একটি if-এর নিচে goto fail; লাইনটি ভুল করে দুবার লেখা ছিল, আর if-এ বন্ধনী ছিল না। দ্বিতীয় goto fail; তাই কোনো শর্ত ছাড়াই সবসময় চলত, আর server-এর signature যাচাইয়ের ধাপটি বাদ পড়ে যেত। ফলে একটি ভুয়া server-ও বৈধ বলে পার পেতে পারত। Indentation দেখে কোডটি একদম ঠিক মনে হতো। এরপর থেকে বহু প্রতিষ্ঠানের coding standard-এ নিয়ম: if, else, loop — সবখানে বন্ধনী, এক লাইন হলেও।

মজার ব্যাপার, ঠিক ওই ধরনের ভুল Java-তে কখনো কখনো compile-এর সময়ই ধরা পড়ে। বন্ধনী ছাড়া if-এর পরে একটি return “বাইরে” চলে গেলে তার পরের লাইনে কখনো পৌঁছানো যায় না, আর Java এমন unreachable statement-কে error বলে (অনলাইন runner-এ যাচাই করা):

prog.java:6: error: unreachable statement
        return "সফল";
        ^
1 error

কিন্তু এটি ভাগ্যের ব্যাপার, নিয়ম নয় — ওপরের "always" উদাহরণে কোনো error আসেনি। নিরাপদ পথ একটাই: সবসময় বন্ধনী। IntelliJ IDEA আর বেশিরভাগ code formatter চাইলে নিজেই বন্ধনী বসিয়ে দেয়।

৩ · শর্ত মানেই boolean — Java-র কড়া নিয়ম

C-তে যেকোনো সংখ্যা শর্ত হতে পারে: ০ মানে মিথ্যা, বাকি সব সত্য। Python-এ আরও ঢিলা — খালি লিস্ট, খালি String, None সবই “মিথ্যার মতো” (একে বলে truthiness). Java এসব মানে না। if-এর বন্ধনীর ভেতরে থাকতে হবে একটি boolean (বা তার wrapper Boolean) — অন্য কিছু নয়।

এই নিয়মের সবচেয়ে বড় লাভ একটি বিখ্যাত টাইপো ধরা: তুলনার ==-এর বদলে ভুল করে একটি = লেখা। C-তে if (x = 5) বৈধ — x-এ 5 বসে, আর 5 শূন্য নয় বলে শর্ত সবসময় সত্য। Java-তে একই লাইন লিখলে (অনলাইন runner, OpenJDK 21):

prog.java:4: error: incompatible types: int cannot be converted to boolean
        if (x = 5) {
              ^
1 error

মেসেজটি পড়ুন: x = 5 একটি assignment, যার ফল int মান 5. আর int থেকে boolean-এ যাওয়ার কোনো পথ Java রাখেনি। একই কারণে if (count) বা if (name) লেখা যায় না — স্পষ্ট করে লিখতে হয় if (count != 0) বা if (name != null).

একটি ফাঁক এখনো আছে। ভ্যারিয়েবলটি নিজেই boolean হলে = টাইপো ধরা পড়ে না: if (done = true) সুন্দরভাবে compile হয় — done-এ true বসে, আর শর্ত সবসময় সত্য। যাচাই করে দেখা গেছে, এমন কোড চলেও আর ভুল পথেও যায়। সমাধান সহজ: boolean-কে কখনো true/false-এর সাথে তুলনা করবেন না। if (done) আর if (!done) লিখুন — পড়তেও সুন্দর, ভুলেরও সুযোগ নেই।

শর্ত লেখার কয়েকটি ভালো অভ্যাস

পর্ব ০৭-এ দেখেছেন, && আর || short-circuit করে — বাঁ দিক দেখেই উত্তর জানা গেলে ডান দিক আর যাচাই করে না। সিদ্ধান্তের কোডে এটি নিরাপত্তার কাজ করে: if (name != null && name.isEmpty()) — name null হলে ডান দিকে যাওয়াই হয় না, তাই কোনো null-এর বিপদ নেই। ক্রম উল্টে দিলে সেই সুরক্ষা হারায়।

  • জটিল শর্তে নাম দিন: boolean isWeekend = day == 6 || day == 7; তারপর if (isWeekend) — কোড নিজেই নিজের ব্যাখ্যা।
  • String তুলনায় == নয়, equals: পর্ব ০৮-এ দেখেছেন, == দেখে দুটি reference একই object কি না। if (cmd.equals("add")) লিখুন। "add".equals(cmd) লিখলে cmd null হলেও exception আসে না।
  • দশমিক সংখ্যা == দিয়ে নয়: 0.1 + 0.2 == 0.3 মিথ্যা — পর্ব ০৬-এর কথা মনে রাখুন। ছোট একটি সীমার ভেতরে আছে কি না দেখুন।
C/C++/Python জানলে

if/else আর পুরোনো switch-এর ব্যাকরণ C/C++-এর প্রায় হুবহু, fall-through সহ। বড় পার্থক্য: Java-তে শর্ত অবশ্যই boolean — C-র মতো সংখ্যা বা pointer, কিংবা Python-এর মতো খালি লিস্টকে “মিথ্যা” ধরা চলে না। Python-এ elif লেখা হয়, Java-তে else if. আর Java-র switch expression অনেকটা Python-এর match-এর কাছাকাছি, যদিও Python-এর match একটি statement, মান ফেরত দেয় না। গভীরে তুলনার জন্য দেখুন C-তে if আর switch আর Python-এ শর্ত আর match.

৪ · else-if সারির ক্রম — প্রথম সত্যটিই জেতে

else-if সারি একটি সরল নিয়মে চলে: ওপর থেকে একটি একটি করে শর্ত দেখা হয়; যে শর্ত প্রথম true, শুধু তার block চলে, তারপর পুরো সারি শেষ। নিচের শর্তগুলো আর দেখাই হয় না। নিয়মটি সরল, কিন্তু এর একটি লুকানো ফল আছে: শর্তগুলো যদি একে অপরের ওপর পড়ে (একটি মান একাধিক শর্তে সত্য হয়), তাহলে ক্রমই উত্তর ঠিক করে।

উদাহরণ হিসেবে নেওয়া যাক বাংলাদেশের মাধ্যমিক ও উচ্চমাধ্যমিক পরীক্ষায় প্রচলিত ৫-পয়েন্ট গ্রেড স্কেল: ৮০ বা বেশি A+ (৫.০০), ৭০–৭৯ A (৪.০০), ৬০–৬৯ A- (৩.৫০), ৫০–৫৯ B, ৪০–৪৯ C, ৩৩–৩৯ D, আর ৩৩-এর নিচে F. নম্বর ৭৫ হলে কী হয়, দেখুন — আর তারপর দেখুন কেউ শর্তগুলো উল্টো ক্রমে লিখলে কী ঘটে:

সঠিক ক্রম · marks = 75শর্ত ১
if (marks >= 80) {          // 75 >= 80 ? false
    grade = "A+";
} else if (marks >= 70) {
    grade = "A";
} else if (marks >= 60) {
    grade = "A-";
} ...
} else {
    grade = "F";
}
সঠিক ক্রম · marks = 75শর্ত ২ — জিতল
if (marks >= 80) {          // false, বাদ
    grade = "A+";
} else if (marks >= 70) {   // 75 >= 70 ? true
    grade = "A";             // এটিই চলল
} else if (marks >= 60) {   // দেখাই হলো না
    grade = "A-";
} ...                       // দেখাই হলো না

grade = "A"  ✔
উল্টো ক্রম · marks = 75বাগ!
if (marks >= 33) {          // 75 >= 33 ? true
    grade = "D";             // এটিই চলল!
} else if (marks >= 40) {   // কখনো দেখা হবে না
    grade = "C";
} else if (marks >= 70) {   // কখনো দেখা হবে না
    grade = "A";
} ...
} else {
    grade = "F";
}

grade = "D"  ✘   (হওয়ার কথা "A")
দুটি নিরাপদ সমাধানঠিক করা
// সমাধান ১: কড়া শর্ত আগে (বড় সীমা ওপরে)
if (marks >= 80)      { grade = "A+"; }
else if (marks >= 70) { grade = "A"; }
...

// সমাধান ২: প্রতিটি শর্ত পূর্ণ পরিসর বলে —
// তখন ক্রম বদলালেও উত্তর বদলায় না
if (marks >= 70 && marks < 80) { grade = "A"; }
  1. প্রথম শর্ত: false. marks = 75. সারির একদম ওপরের শর্ত marks >= 80 — 75 কি 80 বা তার বেশি? না। তাই A+-এর block বাদ, Java পরের else if-এ নামে।
  2. দ্বিতীয় শর্ত: true — খেলা শেষ। 75 >= 70 সত্য। grade হলো "A", আর এখানেই পুরো সারি শেষ। খেয়াল করুন: 75 তো >= 60, >= 50, >= 33 — সবগুলোতেই সত্য! কিন্তু সেগুলো দেখাই হয়নি। “প্রথম সত্য” পেলেই থামা — এটাই else-if-এর নিয়ম, আর এ কারণেই নিচের শর্তে “marks < 80” লেখার দরকার পড়েনি।
  3. উল্টো ক্রম: চুপচাপ ভুল উত্তর। কেউ ছোট সীমা থেকে লিখতে শুরু করলেন: আগে >= 33. এবার 75 প্রথম শর্তেই সত্য, grade হয়ে গেল "D". আরও খারাপ: ৩৩ বা তার বেশি যেকোনো নম্বর — ৯৯ হলেও — "D" পাবে। নিচের শর্তগুলো কখনোই চলবে না। আর javac কোনো error বা warning দেয় না, কারণ ব্যাকরণে কোনো ভুল নেই। এমন বাগ ধরা পড়ে শুধু ভালো টেস্টে।
  4. সমাধান: ক্রম অথবা পূর্ণ পরিসর। একে অপরের ওপর পড়া শর্তে সবচেয়ে কড়াটি (যেটি সবচেয়ে কম মানে সত্য হয়) আগে রাখুন। অথবা প্রতিটি শর্তে পুরো পরিসর লিখুন, যাতে শর্তগুলো আর একে অপরের ওপর না পড়ে — তখন ক্রম বদলালেও ফল বদলায় না। প্রথমটি ছোট, দ্বিতীয়টি বেশি নিরাপদ কিন্তু লম্বা। যেটিই বাছুন, সীমার মানগুলো (৮০, ৭৯, ৩৩, ৩২) দিয়ে টেস্ট করতে ভুলবেন না।

এবার পুরো গ্রেড ক্যালকুলেটর চালিয়ে দেখুন। শর্তের ক্রম সঠিক, প্রতিটি শাখায় বন্ধনী আছে, আর একদম শুরুতে একটি ছোট পাহারাদার শর্ত — যার কথা পরের অংশে বিস্তারিত:

class Main {
    // নম্বর থেকে গ্রেড — ৫-পয়েন্ট স্কেলের একটি উদাহরণ
    static String grade(int marks) {
        if (marks < 0 || marks > 100) {
            return "ভুল নম্বর";              // guard clause
        }
        if (marks >= 80)      { return "A+ (5.00)"; }
        else if (marks >= 70) { return "A (4.00)"; }
        else if (marks >= 60) { return "A- (3.50)"; }
        else if (marks >= 50) { return "B (3.00)"; }
        else if (marks >= 40) { return "C (2.00)"; }
        else if (marks >= 33) { return "D (1.00)"; }
        else                  { return "F (0.00)"; }
    }

    public static void main(String[] args) {
        System.out.println("95 -> " + grade(95));
        System.out.println("75 -> " + grade(75));
        System.out.println("33 -> " + grade(33));
        System.out.println("32 -> " + grade(32));
        System.out.println("105 -> " + grade(105));
    }
}

grade হলো একটি ছোট method — একটি নম্বর নেয়, একটি String ফেরত দেয় (method নিয়ে পুরো আলোচনা পর্ব ১২-এ; আপাতত শুধু দেখুন একই হিসাব পাঁচবার ডাকা হয়েছে)। আউটপুটে আসে A+ (5.00), A (4.00), D (1.00), F (0.00) আর ভুল নম্বর.

টেস্টের মানগুলো ইচ্ছা করে বাছা: ৩৩ আর ৩২ হলো পাস-ফেলের ঠিক সীমা (edge case) — >=-এর জায়গায় ভুল করে > লিখলে ৩৩ ফেল করত, আর এই দুটি মানই সেটি ধরিয়ে দিত। ১০৫ একটি অবৈধ নম্বর; প্রথম if সেটিকে শুরুতেই আলাদা করেছে, নইলে ১০৫ চুপচাপ “A+” পেয়ে যেত। প্রতিটি শাখা return করে বলে আসলে else না লিখলেও চলত — তবু এখানে সারিটি else-if রূপে রাখা হয়েছে, যাতে “একটিই পথ চলবে” কথাটি চোখে পড়ে।

৫ · Nested if বনাম guard clause, আর ternary

if-এর ভেতরে if বসানো যায় — একে বলে nested if. দু-এক স্তর ঠিক আছে, কিন্তু তিন-চার স্তর হলে কোড ডানদিকে সরতে সরতে একটি তীরের ফলার আকার নেয় (প্রোগ্রামাররা মজা করে বলেন “arrow code”). তখন কোন else কোন if-এর, তা খুঁজতে আঙুল দিয়ে বন্ধনী গুনতে হয়।

ধরা যাক একটি মোবাইল ওয়ালেট থেকে টাকা তোলার নিয়ম: PIN ঠিক হতে হবে, অঙ্ক শূন্যের বেশি হতে হবে, আর ব্যালেন্সে কুলাতে হবে। নিচের ডায়াগ্রামে একই নিয়ম দুইভাবে লেখা — দেখুন guard clause দিয়ে কোড কীভাবে সোজা হয়:

শুরু: তিন স্তরের nested ifarrow code
if (pinOk) {
    if (amount > 0) {
        if (amount <= balance) {
            return "সফল";          // আসল কাজ — সবচেয়ে গভীরে
        } else {
            return "ব্যালেন্স কম";
        }
    } else {
        return "ভুল অঙ্ক";
    }
} else {
    return "PIN ভুল";              // শর্ত থেকে অনেক দূরে
}
ধাপ ১: PIN-এর পাহারাশর্ত উল্টে আগে বিদায়
if (!pinOk) { return "PIN ভুল"; }

if (amount > 0) {
    if (amount <= balance) {
        return "সফল";
    } else {
        return "ব্যালেন্স কম";
    }
} else {
    return "ভুল অঙ্ক";
}
ধাপ ২: অঙ্কের পাহারাআরও এক স্তর কম
if (!pinOk)      { return "PIN ভুল"; }
if (amount <= 0) { return "ভুল অঙ্ক"; }

if (amount <= balance) {
    return "সফল";
} else {
    return "ব্যালেন্স কম";
}
শেষ: guard clause রূপসোজা পথ বাঁ কিনারায়
if (!pinOk)            { return "PIN ভুল"; }
if (amount <= 0)       { return "ভুল অঙ্ক"; }
if (amount > balance)  { return "ব্যালেন্স কম"; }

return "সফল";                     // আসল কাজ — কোনো nesting নেই
  1. সমস্যাটি দেখুন। তিনটি শর্ত, তিন স্তর। আসল কাজ ("সফল") সবচেয়ে ভেতরে লুকানো। আর “PIN ভুল” বার্তাটি তার শর্ত if (pinOk) থেকে বারো লাইন নিচে — কোন else কোন if-এর, তা মেলাতে চোখ ওপর-নিচ করতে হয়। চতুর্থ একটি শর্ত (যেমন দৈনিক সীমা) যোগ করলে আরও এক স্তর গভীরে যেতে হবে।
  2. প্রথম পাহারাদার। কৌশলটি হলো শর্ত উল্টে দেওয়া: “PIN ঠিক হলে ভেতরে যাও” বদলে “PIN ভুল হলে এখনই বিদায়”। !pinOk হলে সাথে সাথে return — বাকি কোড ধরে নিতে পারে PIN ঠিক আছে। একটি স্তর কমে গেল, আর বার্তাটি এখন তার শর্তের ঠিক পাশে।
  3. দ্বিতীয় পাহারাদার। একই কৌশল: amount > 0-এর উল্টো amount <= 0. সাবধান — শর্ত উল্টানোর সময় সীমার মানটি ঠিক রাখা চাই। >-এর উল্টো <=, < নয়। এখানেই সবচেয়ে বেশি ভুল হয়, তাই অঙ্ক ০ দিয়ে একবার টেস্ট করে নিন।
  4. শেষ রূপ: পাহারা ওপরে, কাজ নিচে। তিনটি অবৈধ অবস্থা ওপরে সারি বেঁধে বিদায় নিল, আর শেষ লাইনে আসল কাজ — বাঁ কিনারায়, কোনো nesting ছাড়া। চারটি অবস্থার আউটপুট দুই রূপেই হুবহু এক (অনলাইন runner-এ মিলিয়ে দেখা হয়েছে)। নতুন শর্ত যোগ করতে এখন শুধু একটি লাইন লাগে। পর্ব ২৯-এ দেখবেন, পাহারাদার শুধু return নয়, exception-ও ছুড়তে পারে।
কখন guard clause

যখন কিছু অবস্থা “ভুল বা বিশেষ” আর বাকিটা “স্বাভাবিক পথ” — তখন বিশেষগুলো ওপরে বিদায় দিন। কিন্তু যখন সব শাখাই সমান গুরুত্বের (যেমন গ্রেডের সাতটি ধাপ), তখন একটি পরিষ্কার else-if সারি বা switch-ই ভালো। লক্ষ্য একটাই: যিনি পড়বেন, তিনি যেন ওপর থেকে নিচে একবার চোখ বুলিয়েই পুরো নিয়ম বুঝতে পারেন।

Ternary — দুই মানের একটি বাছাই (রিক্যাপ)

পর্ব ০৭-এ দেখা ternary operator হলো if/else-এর একটি ছোট expression রূপ: String result = marks >= 33 ? "পাস" : "ফেল";. পার্থক্যটি গুরুত্বপূর্ণ — if/else একটি statement, যা কাজ করে; ternary একটি expression, যা একটি মান দেয়। তাই ternary সরাসরি ভ্যারিয়েবলে বসানো বা println-এর ভেতরে লেখা যায়। নিয়ম: দুটি সরল মানের মধ্যে বাছাই হলে ternary; শাখার ভেতরে কাজ থাকলে, বা ternary-র ভেতরে আরেকটি ternary লাগলে, if/else বা switch expression ব্যবহার করুন। “statement বনাম expression” — এই পার্থক্যটি মনে রাখুন; পরের অংশে switch-এর আধুনিক রূপ ঠিক এখানেই নতুন।

৬ · পুরোনো switch — case, break আর fall-through

একটি মানকে অনেকগুলো নির্দিষ্ট মানের সাথে মেলাতে হলে else-if সারি লম্বা আর একঘেয়ে হয়ে যায়: if (plan == 1) … else if (plan == 2) … else if (plan == 3) …. এর জন্য Java শুরু থেকেই C-র কাছ থেকে নিয়েছে switch statement. বন্ধনীর ভেতরের মানটিকে বলে selector, আর প্রতিটি সম্ভাব্য মান একটি case label:

switch (plan) {
    case 1:
        System.out.println("Basic");
        break;
    case 2:
        System.out.println("Standard");
        break;
    default:
        System.out.println("Unknown");
}

একটি জরুরি কথা বুঝে নিন: পুরোনো switch-এ case 1: কোনো আলাদা block নয় — এটি শুধু একটি লাফ দেওয়ার জায়গা, অনেকটা বইয়ের পৃষ্ঠা-চিহ্নের মতো। switch মিলে যাওয়া লেবেলে লাফ দেয়, তারপর সেখান থেকে নিচের দিকে সব লাইন চালাতে থাকে — যতক্ষণ না একটি break পায় বা switch শেষ হয়। অন্য case-এর লেবেল পেরিয়ে যাওয়ার সময় সেটিকে আর যাচাই করে না। একে বলে fall-through. break ভুলে গেলে কী হয়, ধাপে ধাপে দেখুন:

1int plan = 1;2switch (plan) {3    case 1:4        System.out.println("Basic");5    case 2:              // break নেই উপরে!6        System.out.println("Standard");7        break;8    case 3:9        System.out.println("Premium");10        break;11    default:12        System.out.println("Unknown");13}
এই মুহূর্তে
plan
কী ঘটছে
একই কাজ, arrow রূপে (Java 14+)fall-through অসম্ভব
switch (plan) {
    case 1 -> System.out.println("Basic");
    case 2 -> System.out.println("Standard");
    case 3 -> System.out.println("Premium");
    default -> System.out.println("Unknown");
}

আউটপুট (plan = 1):
Basic
  1. selector যাচাই, লাফ। plan-এর মান 1. switch সরাসরি case 1: লেবেলে লাফ দেয়। (অনেক ক্ষেত্রে compiler একটি টেবিল বানিয়ে এই লাফ এক ধাপে দেয় — else-if সারির মতো একটি একটি করে তুলনা করতে হয় না।)
  2. Basic ছাপা হলো। লাইন ৪ চলল — এ পর্যন্ত সব ঠিক। প্রোগ্রামারের ইচ্ছা ছিল এখানেই থামা। কিন্তু লাইন ৪-এর পরে কোনো break নেই।
  3. লেবেল পেরিয়ে যাওয়া — fall-through. এটাই মূল মুহূর্ত। case 2: একটি লেবেল মাত্র, কোনো দেয়াল নয়। switch এখানে “plan কি 2?” জিজ্ঞেস করে না — যাচাই হয়েছিল একবারই, শুরুতে। তাই সে নিচে গড়িয়ে চলল।
  4. Standard-ও ছাপা হলো! লাইন ৬ চলল, যদিও plan 2 নয়। গ্রাহক Basic প্ল্যান কিনেছেন, কিন্তু রসিদে দুটি প্ল্যানই উঠল। javac এ নিয়ে কোনো error দেয় না — কারণ fall-through বৈধ Java. (javac-কে -Xlint:fallthrough দিলে অবশ্য একটি warning দেয়; সাধারণভাবে এটি চালু থাকে না।)
  5. break — অবশেষে থামা। লাইন ৭-এর break switch থেকে বের করে আনল। case 3 আর default চলেনি। শিক্ষা: পুরোনো switch-এ প্রতিটি case-এর শেষে break দেওয়া আপনার দায়িত্ব, আর ভুলে গেলে কেউ মনে করিয়ে দেবে না।
  6. Arrow switch: সমস্যাটাই নেই। Java 14 থেকে case 1 -> লেখা যায়। arrow-এর ডান দিকের কাজটি চলে, তারপর switch নিজেই শেষ — পরের case-এ গড়ানোর কোনো উপায় নেই, break লিখতেও হয় না। আউটপুট শুধু Basic. একই switch-এ colon আর arrow মেশানো যায় না; চেষ্টা করলে javac বলে different case kinds used in the switch.

Fall-through কি সবসময় বাগ?

না — কখনো এটি ইচ্ছাকৃত। কয়েকটি মানের জন্য একই কাজ হলে লেবেলগুলো একটার নিচে আরেকটা খালি রাখা হয়: case 6: case 7: তারপর System.out.println("ছুটি"); break;. খালি লেবেল থেকে গড়িয়ে পড়া নিরীহ। বিপদ হয় যখন একটি case-এ কাজ আছে, আর break নেই। পেশাদার কোডে ইচ্ছাকৃত fall-through থাকলে পাশে // fall through comment লেখার প্রথা আছে, যাতে পাঠক বোঝেন এটি ভুল নয়। আধুনিক রূপে একই কাজ আরও পরিষ্কার: case 6, 7 -> ….

switch কোন কোন টাইপে চলে

selector-এর টাইপকবে থেকেমন্তব্য
int, short, byte, charশুরু থেকেC-র উত্তরাধিকার; case 'a', 'b' -> এমনও চলে
Integer, Character ইত্যাদি wrapperJava 5auto-unboxing-এর পর; null হলে বিপদ (অংশ ৮)
enumJava 5case-এ শুধু নাম: case STUDENT, সামনে টাইপের নাম লাগে না
StringJava 7লেখার তুলনা হয় equals-এর মতো, বড়-ছোট হাতের অক্ষর আলাদা
যেকোনো object (pattern দিয়ে)Java 21case Integer i -> — অংশ ৮-এ এক ঝলক, পর্ব ২৬-এ পুরোটা
long, float, double, booleanচলে না (Java 21-এ)javac বলে constant label of type int is not compatible with switch selector type long

String-এর switch সম্পর্কে একটি সূক্ষ্ম কথা: পর্ব ০৮-এ দেখেছেন, দুটি String-এর লেখা এক হলেও == মিথ্যা হতে পারে, কারণ তারা আলাদা object. switch এই ফাঁদে পড়ে না — ভেতরে সে আগে লেখার hashCode() দিয়ে সম্ভাব্য case খুঁজে নেয়, তারপর equals দিয়ে নিশ্চিত হয়। যাচাই করে দেখা গেছে: "ad" + "d" থেকে বানানো String-এর ক্ষেত্রে t == "add" false, কিন্তু case "add" ঠিকই মিলেছে। তবে তুলনাটি হুবহু — "Add" বা " add" মিলবে না। তাই ব্যবহারকারীর ইনপুটে switch করার আগে সাধারণত trim() আর toLowerCase() করে নেওয়া হয় (অংশ ৭-এর কমান্ড পার্সারে দেখবেন)।

৭ · আধুনিক switch — মান ফেরত দেয়, গড়ায় না, সব মান যাচাই করে

পুরোনো switch-এর তিনটি দুর্বলতা ছিল: break ভুললে fall-through, কোনো মান ফেরত দিতে পারে না (তাই আগে একটি ভ্যারিয়েবল বানিয়ে প্রতিটি case-এ বসাতে হতো), আর কোনো case বাদ পড়লে কেউ জানায় না। Java 14-এ স্থায়ী হওয়া switch expression (JEP 361) তিনটিরই সমাধান দেয়। পার্থক্যটি পাশাপাশি দেখুন:

int fare;                     // আগে খালি ভ্যারিয়েবল
switch (type) {
    case "adult":
        fare = 60;
        break;
    case "student":
    case "child":
        fare = 30;
        break;
    default:
        fare = 0;             // ভুলে গেলে? fare অনির্ধারিত
}
int fare = switch (type) {    // switch নিজেই একটি মান
    case "adult" -> 60;
    case "student", "child" -> 30;
    default -> 0;
};                            // শেষে সেমিকোলন — এটি একটি statement-এর অংশ

নতুন রূপের চারটি বৈশিষ্ট্য:

  • Arrow label (->): ডান দিকে একটি expression, একটি { } block, বা একটি throw. কাজ শেষ হলেই switch শেষ — fall-through নেই।
  • এক লেবেলে কয়েকটি মান: case "student", "child" -> — কমা দিয়ে। খালি লেবেলের সারি আর লাগে না।
  • মান ফেরত দেয়: পুরো switch একটি expression, তাই int fare = switch (…) {…}; লেখা যায়, বা সরাসরি return switch (…) {…};.
  • Exhaustiveness: switch expression-কে সব সম্ভাব্য মানের জন্য একটি মান দিতেই হবে। int বা String-এ সম্ভাব্য মান অসীম, তাই default বাধ্যতামূলক। enum-এ সব নাম লিখে দিলে default লাগে না — compiler নিজেই গুনে দেখে।

yield — block থেকে মান ফেরত

arrow-এর ডান দিকে এক লাইনের বেশি কাজ লাগলে { } block লেখা হয়। কিন্তু block থেকে মান ফেরত দেবেন কীভাবে? return লিখলে তো পুরো method থেকে বেরিয়ে যাওয়ার কথা। তাই Java একটি নতুন শব্দ এনেছে: yield — “এই মানটিই পুরো switch-এর মান”। একটি বাসের ভাড়ার নিয়ম দিয়ে দেখুন (নিয়মটি উদাহরণের জন্য বানানো): প্রাপ্তবয়স্ক পুরো ভাড়া, ছাত্রছাত্রী অর্ধেক, শিশু আর প্রবীণ ৭০% ছাড়ে — তবে অন্তত ১০ টাকা।

1static int fare(Passenger p, int base) {2    return switch (p) {3        case ADULT -> base;4        case STUDENT -> base / 2;5        case CHILD, SENIOR -> {6            int discounted = base * 30 / 100;7            yield Math.max(discounted, 10);8        }9    };10}
ভ্যারিয়েবল
p
base
discounted
switch-এর মান
  1. method ডাকা হলো। একজন প্রবীণ যাত্রী, মূল ভাড়া ৬০ টাকা: fare(Passenger.SENIOR, 60). Passenger একটি enum — চারটি নির্দিষ্ট মান: ADULT, STUDENT, CHILD, SENIOR (enum পুরোটা শিখবেন পর্ব ২৪-এ)।
  2. switch-এর selector যাচাই। return switch (p) — এখানে return-এর পরে একটি পুরো switch expression; আগে switch-এর মান বের হবে, তারপর return সেটি ফেরত দেবে। p-এর মান SENIOR.
  3. মিলে যাওয়া লেবেল। case CHILD, SENIOR-এ SENIOR আছে। ADULT আর STUDENT-এর লাইন দুটি চলেনি, আর এই case-এর পরে আর কোনো case-ও চলবে না — arrow রূপে গড়ানোর উপায় নেই। ডান দিকে একটি block, কারণ এখানে দুই ধাপের হিসাব।
  4. block-এর ভেতরের হিসাব। discounted = 60 * 30 / 100 = 18. খেয়াল করুন, গুণ আগে, ভাগ পরে — 30 / 100 আগে করলে int ভাগে 0 হতো (পর্ব ০৭)। discounted ভ্যারিয়েবলটি শুধু এই block-এর ভেতরে বাঁচে।
  5. yield — switch-এর মান ঠিক হলো। Math.max(18, 10) হলো 18, আর yield সেটিকে পুরো switch-এর মান বানাল। block শেষ, discounted ভ্যারিয়েবল হারিয়ে গেল। block থেকে yield না দিলে javac মানে না: switch rule completes without providing a value.
  6. return. switch expression-এর মান 18, return সেটি ফেরত দিল। পুরো যাত্রায় একটি লেবেলই চলেছে, একবারই। আর যেহেতু চারটি enum মানের সবগুলোর case আছে, default লাগেনি — compiler নিজে নিশ্চিত হয়েছে কোনো মান বাদ পড়েনি।

এবার পুরোটা চালিয়ে দেখুন — চার ধরনের যাত্রী:

enum Passenger { ADULT, STUDENT, CHILD, SENIOR }

class Main {
    static int fare(Passenger p, int base) {
        return switch (p) {
            case ADULT -> base;
            case STUDENT -> base / 2;                 // অর্ধেক ভাড়া
            case CHILD, SENIOR -> {
                int discounted = base * 30 / 100;    // ৭০% ছাড়
                yield Math.max(discounted, 10);      // তবে অন্তত ১০ টাকা
            }
        };                                            // default নেই: চারটি মানই আছে
    }

    public static void main(String[] args) {
        System.out.println("Adult: " + fare(Passenger.ADULT, 60));
        System.out.println("Student: " + fare(Passenger.STUDENT, 60));
        System.out.println("Senior: " + fare(Passenger.SENIOR, 60));
        System.out.println("Child (short trip): " + fare(Passenger.CHILD, 20));
    }
}

আউটপুট: Adult: 60, Student: 30, Senior: 18, Child (short trip): 10. শেষ লাইনে ২০ টাকার ছোট যাত্রায় ৩০% হতো মাত্র ৬ টাকা, তাই Math.max সর্বনিম্ন ১০ টাকা বসিয়েছে — yield-এর আগে যেকোনো হিসাব করা যায়।

এখন একটি পরীক্ষা করুন: case CHILD, SENIOR থেকে SENIOR মুছে দিয়ে আবার চালান। কোড আর compile হবে না — javac বলবে switch expression সব সম্ভাব্য মান ঢাকেনি (নিচে পুরো মেসেজ)। কাল কেউ enum-এ FREEDOM_FIGHTER নামে নতুন যাত্রী-ধরন যোগ করলেও একই error আসবে, আর কোথায় কোথায় নিয়ম বসাতে হবে তা compiler-ই দেখিয়ে দেবে। এ কারণেই enum-এর switch expression-এ ইচ্ছা করে default না রাখা ভালো — default থাকলে নতুন মানটি চুপচাপ default-এ চলে যেত। (enum লেখা হয়েছে public ছাড়া, কারণ অনলাইন runner একটি ফাইলেই সব compile করে।)

কোনো মান বাদ পড়লে javac যা বলে (অনলাইন runner-এ যাচাই করা, ওপরের কোডে case CHILD, SENIOR থেকে SENIOR বাদ দিয়ে):

prog.java:5: error: the switch expression does not cover all possible input values
        return switch (p) {
               ^
1 error

একই মেসেজ আসে int বা String-এর switch expression-এ default না দিলে। লক্ষ করুন: এই যাচাই শুধু switch expression-এর (আর pattern ব্যবহার করা switch-এর) জন্য। পুরোনো ধরনের switch statement-এ enum-এর একটি case বাদ পড়লে কোনো error আসে না — সেই case-এ কিছুই চলে না, চুপচাপ।

বাস্তব উদাহরণ: দিনের বাংলা নাম

Java-র নিজের java.time.DayOfWeek একটি enum (MONDAY থেকে SUNDAY). সাতটি case লিখে দিলে default লাগে না:

static String banglaDay(DayOfWeek d) {
    return switch (d) {
        case SATURDAY  -> "শনিবার";
        case SUNDAY    -> "রবিবার";
        case MONDAY    -> "সোমবার";
        case TUESDAY   -> "মঙ্গলবার";
        case WEDNESDAY -> "বুধবার";
        case THURSDAY  -> "বৃহস্পতিবার";
        case FRIDAY    -> "শুক্রবার";
    };
}
// banglaDay(DayOfWeek.FRIDAY) → শুক্রবার, banglaDay(DayOfWeek.of(1)) → সোমবার

অনলাইন runner-এ চালিয়ে মন্তব্যের আউটপুট দুটিই মিলেছে (DayOfWeek.of(1) হলো সোমবার, কারণ ISO নিয়মে সপ্তাহ শুরু সোমবার থেকে)। একটি case মুছলে compile error — দিন ভুলে যাওয়ার কোনো উপায় নেই।

বাস্তব উদাহরণ: ছোট্ট কমান্ড পার্সার

একটি todo অ্যাপ ব্যবহারকারীর কাছ থেকে add, list, done কমান্ড নেয়। String-এর switch expression দিয়ে:

static String command(String cmd) {
    return switch (cmd.trim().toLowerCase()) {
        case "add", "a" -> "নতুন কাজ যোগ হলো";
        case "list"     -> "সব কাজের তালিকা";
        case "done"     -> "কাজটি শেষ চিহ্নিত হলো";
        default         -> "অচেনা কমান্ড: " + cmd.trim();
    };
}
// command("  ADD ") → নতুন কাজ যোগ হলো
// command("fly")    → অচেনা কমান্ড: fly

trim() দুই পাশের ফাঁকা জায়গা ফেলে, toLowerCase() সব ছোট হাতে আনে — তাই " ADD "-ও মিলে গেছে (অনলাইন runner-এ যাচাই করা)। default এখানে শুধু compiler-কে খুশি রাখার জন্য নয়, এটি ব্যবহারকারীকে বলে কী ভুল হলো। পর্ব ২২-এর মাঝপথের প্রজেক্টে এমন একটি পার্সারকে আপনি একটি পূর্ণ CLI টুলে বড় করবেন।

৮ · null, pattern matching-এর এক ঝলক, আর নিরাপদ default

switch-এ null এলে

কমান্ডটি যদি কোথাও থেকে null হয়ে আসে? পুরোনো নিয়মে (case null ছাড়া) switch null পেলেই থেমে যায়। অনলাইন runner-এ যাচাই করা আউটপুট:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.hashCode()" because "<local2>" is null
	at Main.main(prog.java:4)

এই stack trace থেকে দুটি জিনিস শেখা যায়। এক, এটি একটি NullPointerException, ঘটেছে switch-এর লাইনে (৪)। দুই, মেসেজে String.hashCode() — অংশ ৬-এ বলা সেই ভেতরের কৌশলটি নিজের চোখে দেখা গেল: String-এর switch আগে hashCode ডাকে, আর null-এর কোনো hashCode নেই। <local2> হলো compiler-এর বানানো একটি লুকানো ভ্যারিয়েবল, যার নাম class ফাইলে রাখা হয়নি।

Java 21 থেকে (JEP 441) switch-এ সরাসরি case null লেখা যায়:

switch (cmd) {
    case null  -> System.out.println("ফাঁকা কমান্ড");
    case "add" -> System.out.println("add");
    default    -> System.out.println("অচেনা");
}
// cmd = null হলে আউটপুট: ফাঁকা কমান্ড

case null না লিখলে আচরণ আগের মতোই থাকে — NullPointerException. অর্থাৎ নতুন সুবিধাটি পুরোনো কোডের আচরণ বদলায়নি, শুধু চাইলে null-কে স্পষ্টভাবে সামলানোর একটি জায়গা দিয়েছে।

Pattern matching — শুধু মান নয়, টাইপও মেলানো (এক ঝলক)

এতক্ষণ case-এ বসেছে নির্দিষ্ট মান: 1, "add", SENIOR. Java 21-এ স্থায়ী হওয়া pattern matching for switch (JEP 441) দিয়ে case-এ একটি টাইপ বসানো যায়, আর মিললে মানটি সেই টাইপের নতুন ভ্যারিয়েবলে চলে আসে। সাথে when দিয়ে একটি অতিরিক্ত শর্ত — একে বলে guard:

static String describe(Object o) {
    return switch (o) {
        case null                   -> "কিছুই নেই (null)";
        case Integer i when i > 100 -> "বড় সংখ্যা: " + i;
        case Integer i              -> "ছোট সংখ্যা: " + i;
        case String s               -> "লেখা, দৈর্ঘ্য " + s.length();
        default                     -> "অন্য কিছু";
    };
}
// describe(250) → বড় সংখ্যা: 250    describe(7) → ছোট সংখ্যা: 7
// describe("Dhaka") → লেখা, দৈর্ঘ্য 5    describe(3.5) → অন্য কিছু

সব আউটপুট অনলাইন runner-এ (Java 21) যাচাই করা। দুটি জিনিস লক্ষ করুন: এখানেও ক্রম গুরুত্বপূর্ণ — when i > 100-ওয়ালা case সাধারণ Integer i-এর আগে, ঠিক else-if সারির “কড়া শর্ত আগে” নিয়মের মতো (উল্টো ক্রমে লিখলে javac নিজেই error দেয়, কারণ পরেরটি কখনো পৌঁছানো যাবে না)। আর Object-এর সম্ভাব্য টাইপ অসীম, তাই default বাধ্যতামূলক। এই সুবিধার আসল শক্তি দেখা যায় record আর sealed টাইপের সাথে, যেখানে compiler সব উপ-টাইপ জানে বলে default ছাড়াই exhaustiveness যাচাই করতে পারে — সেটি পর্ব ২৬-এর বিষয়।

নিরাপদ default — অসম্ভবকেও সামলান

অনেক সময় ভাবি “এই মান তো কখনো আসবে না”। কিন্তু ইনপুট আসে মানুষের কাছ থেকে, ফাইল থেকে, নেটওয়ার্ক থেকে — আর “কখনো না” একদিন আসেই। default নিয়ে তিনটি নিয়ম মনে রাখুন:

  • int/String-এর switch-এ default চুপচাপ রাখবেন না। অচেনা মান এলে হয় ব্যবহারকারীকে বলুন (কমান্ড পার্সারের মতো), নয়তো একটি exception ছুড়ুন: default -> throw new IllegalArgumentException("Unknown plan: " + plan); — arrow-এর ডান দিকে throw বৈধ। ভুল জায়গায় থেমে যাওয়া ভালো, ভুল উত্তর নিয়ে এগোনো খারাপ।
  • enum-এর switch expression-এ default বাদ দিন। তাহলে নতুন enum মান যোগ হলে compiler আপনাকে প্রতিটি switch দেখিয়ে দেবে। (তবু যদি কোনো পুরোনো compile করা কোড নতুন মান পায়, compiler নিজেই একটি লুকানো default বসায়, যা exception ছোড়ে — ভুল মান নিয়ে চুপচাপ চলে না।)
  • “অসম্ভব” শাখাও লিখে রাখুন। else-if সারির শেষ else বা switch-এর default-এ অন্তত একটি স্পষ্ট বার্তা দিন — ছয় মাস পরে বাগ খুঁজতে এটিই প্রথম সূত্র দেবে।
পরীক্ষা ও ইন্টারভিউতে
  • Java-তে if (x = 5) লিখলে কী হয়? — compile error: int cannot be converted to boolean; Java-র শর্ত অবশ্যই boolean.
  • switch-এ break না দিলে কী হয়? — পুরোনো colon রূপে fall-through: মিলে যাওয়া case-এর পরের case-গুলোর কোডও চলে, যতক্ষণ না break বা switch-এর শেষ আসে; arrow রূপে fall-through হয় না।
  • switch কোন টাইপে চলে না? — long, float, double আর boolean (Java 21 পর্যন্ত); String চলে Java 7 থেকে, enum Java 5 থেকে।
  • yield আর return-এর পার্থক্য? — yield শুধু switch expression-এর block থেকে মান দেয়; return পুরো method থেকে বেরিয়ে যায়।
  • switch expression-এ default কখন লাগে না? — যখন সব সম্ভাব্য মান case-এ আছে, যেমন enum-এর সব নাম; int বা String-এ default বাধ্যতামূলক।

৯ · AI-এর যুগে — AI-এর লেখা সিদ্ধান্ত যাচাই করা

সিদ্ধান্তের কোড AI খুব দ্রুত লেখে, আর দেখতে প্রায় সবসময় ঠিক লাগে। সমস্যা হলো, এই পর্বের প্রায় সব বাগ — ক্রমের ভুল, হারানো break, ভুলে যাওয়া default — compile হয়ে যায়, চলেও। AI-এর প্রশিক্ষণ-ডেটায় কয়েক দশকের পুরোনো Java কোড আছে, তাই সে প্রায়ই পুরোনো ধাঁচে লেখে।

Compile হলো মানে ব্যাকরণ ঠিক।
প্রতিটি পথ ঠিক কি না, তা দেখার দায়িত্ব আপনার।

AI সাধারণত যে ভুলগুলো করে

AI যা লেখেসমস্যাআপনি যা চাইবেন
colon রূপের switch, কোনো case-এ break নেইfall-through — দুটি case-এর কাজ একসাথেarrow রূপের switch (Java 14+)
দশ-বারো শাখার if-else সারি, প্রতিটিতে cmd.equals("…")লম্বা, ক্রমের ভুলের ঝুঁকি, case বাদ পড়লে কেউ জানায় নাswitch expression; অনেক নির্দিষ্ট মান-জোড়া হলে Map (পর্ব ৩১)
enum-এর switch-এ অপ্রয়োজনীয় defaultনতুন enum মান চুপচাপ default-এ চলে যায়default ছাড়া সব case — compiler যাচাই করবে
default নেই, বা default-এ শুধু breakঅচেনা ইনপুট নিঃশব্দে উপেক্ষাবার্তা বা IllegalArgumentException
পরিসরের শর্ত ভুল ক্রমে, বা >/>= গুলিয়েসীমার মানে ভুল ফলসীমার মান দিয়ে টেস্ট
বন্ধনী ছাড়া এক লাইনের ifপরে লাইন যোগ করলে “goto fail” ধাঁচের বাগসবখানে বন্ধনী

নিচের প্রম্পটে Java version আর নিয়মগুলো আগেই বলে দিন — আর সবচেয়ে জরুরি, AI-কে দিয়ে সীমার মানের একটি টেস্ট-তালিকা বানিয়ে নিন, যা আপনি নিজে মিলিয়ে দেখবেন:

আমি Java 21 ব্যবহার করছি। নিচের নিয়মে একটি method লিখে দাও: [এখানে নিয়মটি লেখো — যেমন: নম্বর ০–১০০ থেকে গ্রেড; ৮০+ A+, ৭০–৭৯ A, ... ৩৩-এর নিচে F; ০–১০০-এর বাইরে হলে IllegalArgumentException] শর্ত: ১. অনেক নির্দিষ্ট মানের মধ্যে বাছাই হলে arrow রূপের switch expression ব্যবহার করো, colon-রূপের switch আর break নয়। ২. পরিসরের শর্ত হলে if / else if লেখো, প্রতিটি শাখায় { } বন্ধনী দাও, আর কড়া শর্ত আগে রাখো। ৩. অবৈধ ইনপুট method-এর শুরুতে guard clause দিয়ে বাদ দাও। ৪. enum-এর switch expression-এ default দেবে না; int বা String-এ default-এ স্পষ্ট বার্তা বা exception দাও। ৫. কোডের পরে একটি টেবিল দাও: প্রতিটি সীমার মান (যেমন -1, 0, 32, 33, 79, 80, 100, 101) আর প্রত্যাশিত ফল।

AI-এর লেখা সিদ্ধান্ত-কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে কোড-লেখক থেকে সিস্টেম-চিন্তক বানাবে।

প্র ০১C-তে যেকোনো সংখ্যা শর্ত হতে পারে, অথচ Java শুধু boolean মানে। Java-র নকশাকারেরা কেন এই কড়াকড়ি বেছে নিলেন, আর এর দাম কী?

C-র জন্ম ১৯৭০-এর দশকের শুরুতে, যখন প্রতিটি byte আর প্রতিটি CPU নির্দেশ দামি। C-তে আলাদা boolean টাইপই ছিল না (অনেক পরে, C99-এ _Bool আসে); “সত্য” মানে শুধু “শূন্য নয়”। এতে কোড ছোট হতো, আর যন্ত্রের কাছাকাছি থাকত। কিন্তু দাম ছিল একটি বিখ্যাত টাইপো: if (x == 0)-এর বদলে if (x = 0). দ্বিতীয়টিও বৈধ C — x-এ 0 বসায়, আর শর্ত সবসময় মিথ্যা। এই ভুল এত সাধারণ ছিল যে C প্রোগ্রামাররা উল্টো করে লেখার অভ্যাস করতেন: if (0 == x) — একে মজা করে বলা হয় “Yoda condition”, কারণ 0 = x লিখলে compile হয় না। পরে GCC-র মতো compiler এমন লাইনে warning দিতে শুরু করে।

এই টাইপো শুধু নিরীহ ভুল নয়, ইচ্ছাকৃত আক্রমণেও ব্যবহার হয়েছে। ২০০৩ সালে Linux kernel-এর একটি source mirror-এ কেউ গোপনে একটি লাইন ঢোকানোর চেষ্টা করেছিল, যেখানে current->uid = 0 এমনভাবে একটি শর্তের ভেতরে লুকানো ছিল যে সেটি দেখতে তুলনার মতো, কিন্তু আসলে প্রোগ্রামটিকে root (সর্বোচ্চ ক্ষমতা) দিয়ে দিত। পরিবর্তনটি সময়মতো ধরা পড়ে। এক অক্ষরের পার্থক্য কতটা বিপজ্জনক হতে পারে, এটি তার বিখ্যাত উদাহরণ।

১৯৯০-এর দশকে James Gosling-এর দল যখন Java বানায়, তাদের ঘোষিত লক্ষ্য ছিল C/C++-এর চেহারা রাখা কিন্তু সাধারণ বাগের উৎসগুলো বন্ধ করা — pointer arithmetic, হাতে মেমরি মুক্ত করা, আর এমন অস্পষ্ট রূপান্তর। তাই Java-তে boolean একটি সম্পূর্ণ আলাদা টাইপ, যা int-এ বা int থেকে কোনোভাবেই রূপান্তর হয় না — cast দিয়েও না। ফলে if (x = 0) ধরনের টাইপো Java-তে compile-ই হয় না।

দাম কী? সামান্য বেশি টাইপ করা: if (count)-এর বদলে if (count != 0), if (ptr)-এর বদলে if (obj != null). আর সুরক্ষাটি সম্পূর্ণ নয় — boolean ভ্যারিয়েবলে if (done = true) এখনো compile হয়। তবু বেশিরভাগ প্রোগ্রামার মানেন, এই বাড়তি অক্ষরগুলো পড়ার স্পষ্টতাও বাড়ায়: শর্তটি ঠিক কী যাচাই করছে, তা লেখাতেই বলা থাকে।

মূল উপলব্ধি: ভালো টাইপ-সিস্টেম একটি পুরো শ্রেণির ভুলকে “অসম্ভব” বানিয়ে দেয়। প্রতিবার সাবধান থাকার চেয়ে নিয়ম দিয়ে ভুলের দরজা বন্ধ করা অনেক বেশি নির্ভরযোগ্য।

প্র ০২Java-র switch শুরুতে String-এ চলত না, এলো Java 7-এ (২০১১)। এত সাধারণ একটি সুবিধা আসতে প্রায় ষোলো বছর লাগল কেন, আর ভেতরে এটি কীভাবে কাজ করে?

Java-র প্রথম switch ছিল সরাসরি C-র উত্তরাধিকার, আর C-র switch তৈরি হয়েছিল একটি যন্ত্র-স্তরের কৌশল মাথায় রেখে: case-এর মানগুলো ছোট পূর্ণসংখ্যা হলে compiler একটি “jump table” বানাতে পারে — selector-এর মানকে টেবিলের index ধরে এক ধাপে সঠিক জায়গায় লাফ। Java-র bytecode-এও ঠিক এই উদ্দেশ্যে দুটি নির্দেশ আছে: tableswitch (ঘন মানের জন্য টেবিল) আর lookupswitch (ছড়ানো মানের জন্য সাজানো তালিকা)। দুটিই শুধু int নিয়ে কাজ করে। String কোনো ছোট পূর্ণসংখ্যা নয়, তাই প্রথম দিকের Java-তে এর জায়গা ছিল না।

Java 5 (২০০৪)-এ enum এলো, আর সাথে enum-এর switch — ভেতরে প্রতিটি enum মানের একটি ক্রমিক নম্বর (ordinal) দিয়ে সেটিকে int switch-এ রূপান্তর করা হয়। String-এর জন্য অনুরোধ বহু বছর ধরেই ছিল, কিন্তু Java ভাষায় ছোট পরিবর্তনও সাবধানে আনে। শেষে Java 7-এ “Project Coin” নামে একগুচ্ছ ছোট ভাষাগত উন্নতির অংশ হিসেবে এটি আসে — একই প্রকল্পে এসেছিল try-with-resources, diamond (<>) আর সংখ্যার ভেতরে underscore (1_000_000).

ভেতরের কৌশলটি চতুর। javac String-এর switch-কে দুই ধাপের int switch-এ রূপান্তর করে। প্রথম ধাপে selector-এর hashCode() বের করে, সেই সংখ্যা দিয়ে একটি int switch চালায়। কিন্তু দুটি আলাদা String-এর hashCode একই হতে পারে (collision), তাই মিলে যাওয়া প্রতিটি সম্ভাবনায় আবার equals() দিয়ে নিশ্চিত হয়। তারপর দ্বিতীয় একটি int switch আসল case-এর কোড চালায়। এই পর্বের অংশ ৮-এ null-এর stack trace-এ String.hashCode() দেখা গিয়েছিল — সেটি এই রূপান্তরেরই চিহ্ন।

এই নকশার একটি গুরুত্বপূর্ণ ফল: নতুন ভাষা-সুবিধার জন্য JVM বা bytecode-এ কোনো পরিবর্তন লাগেনি; সব কাজ compiler করেছে। ফলে নতুন কোড পুরোনো JVM-এর একই নির্দেশ দিয়েই চলে। Java বহু সুবিধা এভাবে এনেছে — ভাষায় নতুন রূপ, কিন্তু নিচে পুরোনো, পরীক্ষিত ভিত্তি।

মূল উপলব্ধি: যে সুবিধা ওপর থেকে সরল দেখায়, তার পেছনে প্রায়ই compiler-এর একটি চতুর রূপান্তর থাকে। ভেতরটা জানলে আচরণের অদ্ভুত দিকগুলো — যেমন null-এ hashCode-এর exception — আর রহস্য থাকে না।

প্র ০৩switch expression স্থায়ী হওয়ার আগে দুটি preview পর্ব পেরিয়েছিল (JEP 325, 354, তারপর 361). এই পথে কী বদলেছিল, আর এই ধাপে ধাপে আনার পদ্ধতি থেকে কী শেখা যায়?

Java 10 (২০১৮) থেকে Java প্রতি ছয় মাসে একটি নতুন সংস্করণ প্রকাশ করে। কাছাকাছি সময়ে আসে preview feature ব্যবস্থা: একটি নতুন ভাষা-সুবিধা পুরোপুরি তৈরি, কিন্তু স্থায়ী নয় — --enable-preview flag দিয়ে চালু করে মানুষ ব্যবহার করে দেখে, মতামত দেয়, আর পরের সংস্করণে নকশা বদলাতে পারে। switch expression এই ব্যবস্থার প্রথম দিকের বড় উদাহরণগুলোর একটি।

  • Java 12 (মার্চ ২০১৯) — JEP 325, প্রথম preview: arrow label, কমা দিয়ে একাধিক মান, আর expression হিসেবে switch এলো। block থেকে মান ফেরত দেওয়ার জন্য তখন লেখা হতো break value; — অর্থাৎ break শব্দটিকেই নতুন কাজ দেওয়া হয়েছিল।
  • Java 13 (সেপ্টেম্বর ২০১৯) — JEP 354, দ্বিতীয় preview: মতামতে দেখা গেল, break value বিভ্রান্তিকর — পুরোনো switch আর loop-এ break-এর অর্থ “বেরিয়ে যাও”, মান দেওয়া নয়, আর break label-এর সাথেও গুলিয়ে যায়। তাই নতুন শব্দ আনা হলো: yield.
  • Java 14 (মার্চ ২০২০) — JEP 361, স্থায়ী: আর কোনো পরিবর্তন ছাড়া সুবিধাটি ভাষার স্থায়ী অংশ হলো।

yield নিয়ে একটি সূক্ষ্ম সমস্যাও ছিল: পুরোনো কোডে কেউ হয়তো yield নামে ভ্যারিয়েবল বা method রেখেছেন। তাই yield-কে পুরোপুরি সংরক্ষিত keyword না করে একটি “প্রাসঙ্গিক” শব্দ (contextual keyword) বানানো হয় — শুধু নির্দিষ্ট জায়গায় এর বিশেষ অর্থ। লাখো পুরোনো প্রজেক্ট যেন ভেঙে না যায়, সেটাই Java-র প্রথম শর্ত।

এই অভিজ্ঞতা দেখায় preview ব্যবস্থার মূল্য। প্রথম নকশায় break value খারাপ ছিল না — কাজ করত। কিন্তু হাজার হাজার প্রোগ্রামারের হাতে পড়ার পরেই বোঝা গেল, এটি পড়তে বিভ্রান্তিকর। ভাষায় যা একবার স্থায়ী হয়, তা আর ফেরানো যায় না; তাই স্থায়ী করার আগে বাস্তব ব্যবহারে পরীক্ষা জরুরি। একই পথে পরে এসেছে record, sealed class, আর pattern matching for switch.

মূল উপলব্ধি: নকশার প্রথম সংস্করণ প্রায়ই নিখুঁত হয় না। যে সিদ্ধান্ত ফেরানো যায় না, সেটি নেওয়ার আগে ছোট পরিসরে পরীক্ষা আর মতামতের সুযোগ রাখুন — সফটওয়্যারে, আর জীবনেও।

প্র ০৪Java 21-এ pattern matching for switch (JEP 441) স্থায়ী হলো। Brian Goetz একে “data-oriented programming”-এর অংশ বলেছেন। এর মানে কী, আর এটি object-oriented চিন্তা থেকে কোথায় আলাদা?

প্রচলিত object-oriented নকশায় (পর্ব ২০-এ polymorphism দেখবেন) “টাইপ অনুযায়ী আলাদা আচরণ” আসে method override থেকে: প্রতিটি class নিজের area() জানে, আর কোডে কেউ জিজ্ঞেস করে না “এই object কোন টাইপের?”। এটি দারুণ কাজ করে যখন নতুন টাইপ ঘন ঘন যোগ হয়, কিন্তু কাজের তালিকা স্থির। উল্টো পরিস্থিতিও আছে: টাইপের তালিকা স্থির (যেমন একটি payment হয় Cash, Card অথবা MobileWallet — এর বাইরে কিছু নয়), কিন্তু সেই ডেটার ওপর নতুন নতুন কাজ যোগ হয় — রিপোর্ট, ফি হিসাব, ঝুঁকি যাচাই। প্রতিটি নতুন কাজের জন্য সব class-এ নতুন method বসানো তখন ঝামেলার।

২০২২ সালে Java-র ভাষা-স্থপতি Brian Goetz “Data Oriented Programming in Java” নামের একটি প্রবন্ধে এই দ্বিতীয় ধারার কথা বলেন। মূল ভাবনা: ডেটাকে সরল, অপরিবর্তনীয় বাহক হিসেবে মডেল করুন (record), সম্ভাব্য রূপগুলোর তালিকা বন্ধ করে দিন (sealed interface), আর কাজগুলো লিখুন ডেটার রূপ দেখে (pattern matching for switch). তিনটি টুকরো ধাপে ধাপে এসেছে: record স্থায়ী Java 16-এ, sealed class Java 17-এ, আর record pattern (JEP 440) ও pattern matching for switch (JEP 441) Java 21-এ। pattern matching for switch নিজেও স্থায়ী হওয়ার আগে Java 17 থেকে 20 পর্যন্ত চারবার preview হয়েছিল।

এখানে exhaustiveness-ই আসল শক্তি। sealed interface-এর সব উপ-টাইপ compiler জানে। তাই switch-এ সবগুলোর case থাকলে default লাগে না, আর কাল কেউ নতুন একটি payment ধরন যোগ করলে প্রতিটি অসম্পূর্ণ switch compile error দেবে — এই পর্বের enum-এর উদাহরণটির মতোই, কিন্তু অনেক বেশি সমৃদ্ধ ডেটার জন্য। Functional ভাষাগুলোতে (ML, Haskell, Scala) এই ধারণা বহু দিনের, যাকে বলে algebraic data type আর pattern matching. Java এটি নিজের পরিচিত ব্যাকরণে এনেছে।

এর মানে এই নয় যে object-oriented নকশা বাতিল। দুটি হাতিয়ার দুই ধরনের সমস্যার জন্য: আচরণ বেশি বদলালে polymorphism, ডেটার রূপ স্থির কিন্তু কাজ বদলালে pattern matching. ভালো Java প্রোগ্রামার দুটিই চেনেন। পর্ব ২৬-এ sealed class আর record pattern দিয়ে এর পূর্ণ রূপ দেখবেন।

মূল উপলব্ধি: switch এখন আর শুধু “কোন সংখ্যা” জিজ্ঞেস করে না — “ডেটাটির আকার কী” জিজ্ঞেস করে। কোন অক্ষ বরাবর পরিবর্তন বেশি আসবে (নতুন টাইপ, না নতুন কাজ), সেটি ভেবেই নকশা বাছুন।

প্র ০৫fall-through এত বাগের জন্ম দেয়, তবু C-তে এটি ডিফল্ট কেন? “Duff’s device” কী, আর Java কেন এই আচরণ রেখে দিয়েছিল?

C-র switch-কে বুঝতে হলে মনে রাখুন, C নকশা করা হয়েছিল যন্ত্রের খুব কাছাকাছি থাকার জন্য। C-র switch আসলে একটি “হিসাব করা goto”: case লেবেলগুলো কোডের ভেতরে কয়েকটি ঠিকানা মাত্র, আর switch শুধু ঠিক ঠিকানায় লাফ দেয়। লাফের পর CPU যেমন পরের নির্দেশে স্বাভাবিকভাবে এগোয়, কোডও তেমন নিচে গড়ায় — থামতে চাইলে স্পষ্ট করে break (আসলে switch-এর বাইরে আরেকটি লাফ) লাগে। অর্থাৎ fall-through কোনো “সুবিধা” হিসেবে যোগ করা হয়নি; এটি লেবেল-ভিত্তিক নকশার স্বাভাবিক ফল।

এই নকশার সবচেয়ে বিখ্যাত (আর কুখ্যাত) ব্যবহার হলো Duff’s device. ১৯৮৩ সালে Lucasfilm-এ কাজ করার সময় Tom Duff একটি কপি-loop দ্রুত করতে চাইছিলেন। কৌশলটি ছিল “loop unrolling” — loop-এর প্রতি ঘুরে একটির বদলে আটটি কপি, যাতে loop-এর শর্ত যাচাই কম হয়। কিন্তু মোট সংখ্যা আটের গুণিতক না হলে বাকিগুলো কী হবে? Duff একটি switch-এর case লেবেলগুলো একটি do-while loop-এর ভেতরে ছড়িয়ে দিলেন: switch প্রথমবার বাকি অংশের হিসাবে মাঝখানের কোনো লেবেলে লাফ দেয়, তারপর fall-through দিয়ে নিচে গড়িয়ে loop-এ ঢুকে পড়ে। কোডটি বৈধ C, কাজও করে — আর প্রথমবার দেখে প্রায় সবাই চমকে যান। Duff নিজে মজা করে লিখেছিলেন, এটি fall-through-এর পক্ষে না বিপক্ষে যুক্তি, তা তিনি নিশ্চিত নন।

বাস্তবে, ইচ্ছাকৃত fall-through খুব কম ক্ষেত্রেই লাগে; বেশিরভাগ case শেষ হয় break দিয়ে। তবু ১৯৯০-এর দশকে Java এই আচরণ হুবহু রেখে দেয়, কারণ Java-র বড় লক্ষ্য ছিল C/C++ প্রোগ্রামারদের কাছে পরিচিত থাকা — একই দেখতে switch ভিন্ন আচরণ করলে সেটি আরও বিপজ্জনক হতো। ফলে প্রায় পঁচিশ বছর ধরে Java প্রোগ্রামারদের break মনে রাখতে হয়েছে; javac-এ -Xlint:fallthrough warning আর বিভিন্ন static analysis টুল এই ফাঁক আংশিক ঢেকেছে।

Java-র সমাধানটি লক্ষ করার মতো: পুরোনো switch-এর আচরণ বদলানো হয়নি (তাতে লাখো পুরোনো প্রোগ্রাম ভাঙত), বরং পাশে একটি নতুন রূপ — arrow label — যোগ হয়েছে, যেখানে fall-through নেই-ই। C#, Swift, Go-র মতো নতুন ভাষাগুলোও ভিন্ন ভিন্ন উপায়ে অনিচ্ছাকৃত fall-through বন্ধ করেছে।

মূল উপলব্ধি: একটি ভাষার অদ্ভুত আচরণ প্রায়ই তার জন্মকালের যন্ত্র আর সীমাবদ্ধতার ছাপ। পুরোনো আচরণ ভাঙা না করে নিরাপদ বিকল্প যোগ করা — বড় সিস্টেম বিবর্তনের এটাই পরিণত পথ।

অনুশীলন

বেশিরভাগ কাজে নতুন কোড লিখতে হবে না — ওপরের কোড বদলে দেখা আর ভাবাই আসল কাজ।

  1. ক্রম ভেঙে দেখুন: গ্রেড ক্যালকুলেটরে (অংশ ৪) marks >= 80 আর marks >= 70 লাইন দুটির জায়গা বদলে চালান। কোন কোন নম্বরের ফল বদলাল, আর কেন? তারপর আগের অবস্থায় ফেরান।

    এখন প্রথম শর্ত marks >= 70. 95 সেখানেই সত্য, তাই ফল হয় A (4.00) — A+ আর কেউ পাবে না, কারণ ৮০+ সব নম্বর ৭০+-এর শর্তেও সত্য। 75, 33, 32, 105-এর ফল বদলায় না (75 এমনিতেই A; বাকিরা এই দুই শর্তে মিথ্যা, আর 105 guard clause-এ আগেই বাদ)। লক্ষ করুন, compiler কিছুই বলেনি — এই বাগ ধরেছে শুধু ৯৫-এর মতো একটি টেস্ট-মান। শিক্ষা: প্রতিটি শাখার অন্তত একটি মান দিয়ে টেস্ট করুন।

  2. আউটপুট অনুমান করুন: না চালিয়ে বলুন, int day = 2; হলে নিচের পুরোনো switch কী ছাপবে — case 1: print("A"); case 2: print("B"); case 3: print("C"); break; default: print("D"); (এখানে print মানে System.out.print). আর day = 5 হলে?

    day = 2: BC. switch সরাসরি case 2-এ লাফ দেয় (case 1 চলে না), "B" ছাপে, break না থাকায় case 3-এ গড়িয়ে "C" ছাপে, তারপর break. day = 5: কোনো case মেলে না, তাই default-এ যায় — D. মনে রাখুন, fall-through শুরু হয় মিলে যাওয়া লেবেল থেকে নিচের দিকে; ওপরের case কখনো চলে না।

  3. Exhaustiveness নিজে দেখুন: ভাড়ার কোডে (অংশ ৭) প্রথমে CHILD, SENIOR থেকে SENIOR মুছে চালান। তারপর SENIOR ফিরিয়ে এনে enum-এ একটি নতুন মান FREEDOM_FIGHTER যোগ করে চালান। দুই ক্ষেত্রে কী হলো? শেষে default -> base; যোগ করলে কী বদলায় — সেটি কি ভালো সমাধান?

    দুই ক্ষেত্রেই একই compile error: the switch expression does not cover all possible input values. প্রথমবার SENIOR, দ্বিতীয়বার FREEDOM_FIGHTER-এর case নেই। default -> base; যোগ করলে error চলে যায় — কিন্তু তখন মুক্তিযোদ্ধা যাত্রী চুপচাপ পুরো ভাড়া দেবেন, আর কোনো সতর্কতা আসবে না। ঠিক সমাধান হলো তাঁর জন্য আলাদা নিয়ম লেখা: case FREEDOM_FIGHTER -> 0; (যদি নিয়ম হয় বিনা ভাড়া)। enum-এর switch-এ default যোগ করা মানে compiler-এর দেওয়া পাহারাটাই সরিয়ে দেওয়া।

  4. রূপান্তর করুন (কোড লেখার কাজ): নিচের পুরোনো switch-টিকে একটি switch expression-এ বদলান, যা একটি String ফেরত দেয়। switch (month) { case 12: case 1: case 2: season = "শীত"; break; case 3: case 4: case 5: season = "গ্রীষ্ম"; break; default: season = "অন্য ঋতু"; }

    একটি সম্ভাব্য রূপ:

    String season = switch (month) {
        case 12, 1, 2 -> "শীত";
        case 3, 4, 5  -> "গ্রীষ্ম";
        default       -> "অন্য ঋতু";
    };

    খালি লেবেলের সারি কমা দিয়ে এক লাইনে এলো, break উধাও, আর season ভ্যারিয়েবলটি এখন একবারেই মান পায় — কোনো শাখায় বসাতে ভুলে যাওয়ার সুযোগ নেই। month int বলে default বাধ্যতামূলক; বাদ দিলে compile error. আরও ভালো করতে চাইলে ১–১২-এর বাইরের মানের জন্য default-এ exception ছুড়তে পারেন, আর বাকি মাসগুলোর জন্য আলাদা case যোগ করতে পারেন।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমmarks = 75 নিয়ে একটি সঠিক ক্রমের গ্রেড-সারি (৮০+ A+, ৭০+ A, …) চলার ধাপগুলো সাজান (▲▼ চেপে সরান)

  1. 🔎 প্রথম শর্ত marks >= 80 যাচাই — false
  2. ⏭️ পরের else if (marks >= 70)-এ নামা
  3. ✅ 75 >= 70 true — grade = "A"
  4. 🚫 নিচের সব শর্ত বাদ, সারির পরের লাইনে যাওয়া
  5. 🖨️ grade ছাপা: A

Java-তে int x = 3; if (x = 5) { … } লিখলে কী হয়?

  • x-এ 5 বসে, আর শর্ত সবসময় সত্য
  • Java এটিকে x == 5 হিসেবে পড়ে, শর্ত মিথ্যা
  • compile error: int cannot be converted to boolean
  • compile হয়, কিন্তু চালানোর সময় exception আসে

int plan = 1; switch (plan) { case 1: System.out.print("A"); case 2: System.out.print("B"); break; case 3: System.out.print("C"); } — আউটপুট কী?

  • A
  • AB
  • ABC
  • compile error, কারণ case 1-এ break নেই

int score = 75; নিচের সারিতে result-এর মান কী হবে? if (score >= 40) result = "C"; else if (score >= 60) result = "A-"; else if (score >= 70) result = "A"; else result = "F";

  • A
  • A-
  • F
  • C

Java-র switch expression-এ { } block থেকে মান ফেরত দিতে কোন শব্দ ব্যবহার হয়?

  • yield
  • return
  • break value
  • give

enum Size { S, M, L } থাকলে int price = switch (size) { case S -> 10; case M -> 20; }; লিখলে কী হয়?

  • compile হয়; L এলে price হয় 0
  • compile হয়; L এলে চালানোর সময় exception
  • compile error: সব সম্ভাব্য মান ঢাকা হয়নি
  • L এলে M-এর case-এ গড়িয়ে price হয় 20

Java 21-এ নিচের কোন টাইপের selector দিয়ে switch লেখা যায় না?

  • String
  • long
  • char
  • enum

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Java-র শর্ত অবশ্যই boolean — if (x = 5) compile হয় না; boolean ভ্যারিয়েবলে if (done) লিখুন, == true নয়।
  2. else-if সারিতে প্রথম সত্য শর্তটিই জেতে — একে অপরের ওপর পড়া শর্তে কড়াটি আগে, আর সীমার মান দিয়ে টেস্ট।
  3. সবখানে বন্ধনী, এক লাইনের if-এও; গভীর nesting-এর বদলে guard clause দিয়ে অবৈধ অবস্থা আগে বিদায় দিন।
  4. পুরোনো switch-এ break ভুললে fall-through; arrow রূপে (case 1, 2 ->) গড়ানো নেই, আর switch expression মান ফেরত দেয় — block থেকে yield দিয়ে।
  5. switch expression exhaustive হতে হয়: enum-এ সব case দিন (default নয়), int/String-এ default-এ স্পষ্ট বার্তা বা exception; null-এর জন্য Java 21-এর case null.

পরের পর্বে: সিদ্ধান্ত দিয়ে প্রোগ্রাম পথ বাছতে শিখল। কিন্তু একই কাজ একশোবার করতে হলে? for, while, do-while, for-each, আর break ও continue — যার একটি (break) আজই switch-এ দেখলেন। পুনরাবৃত্তির জগতে ঢুকব।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান