5·1

البوابة تسأل — boolean والمقارنة

صباح يوم جامعي: تصل إلى بوابة الحرم فتجد حارسًا ذكيًا لا يفهم إلا لغة واحدة — «نعم» أو «لا».

الحارس الذكي عند البوابة يسألك أسئلة سريعة: «هل عمرك أكبر من 17؟»، «هل معك البطاقة؟»، «هل رقم قاعتك يساوي 204؟». وهو لا يقبل إجابة «ربما» ولا «تقريبًا» — الإجابة إما true (نعم) وإما false (لا)، ولا ثالث لهما. هذا بالضبط ما يحتاجه أي برنامج ليتخذ قرارًا.

في جافا يوجد نوع بيانات مخصص لهذه الإجابات اسمه boolean (قيمة منطقية). متغير من هذا النوع يحمل قيمة واحدة من اثنتين فقط: true أو false — تُكتبان بحروف صغيرة ودون تنصيص.

طرفة من الحرمالحارس لا يقبل «ربما» — إما true أو false. ومن يحاول إقناعه بـ «شبه صحيح» سيبقى واقفًا عند البوابة.

أسئلة الحارس تُكتب في جافا بـ عوامل المقارنة (relational operators). كل عامل يأخذ قيمتين ويعيد جوابًا واحدًا: true أو false:

العاملسؤال الحارسمثالالنتيجة
==هل الاثنان متساويان؟8 == 8true
!=هل هما مختلفان؟5 != 5false
>هل الأول أكبر؟7 > 3true
<هل الأول أصغر؟3 < 3false
>=هل الأول أكبر أو يساوي؟4 >= 5false
<=هل الأول أصغر أو يساوي؟2 <= 9true

جرّب ميزان الحارس بنفسك — المقارنة التالية تسأل هل الرقم الأول أكبر من الثاني:

boolean r = 10 > 6; // true

والآن مثال كامل يطبع نتائج أربع مقارنات. لاحظ أن المقارنة نفسها تُكتب بين قوسين لتُحسب أولًا، ثم تلتصق نتيجتها (true أو false) بالنص الذي قبلها:

ComparisonDemo.java
public class ComparisonDemo {    public static void main(String[] args) {        int num1 = 10;        int num2 = 6;        System.out.println("num1 > num2: " + (num1 > num2));        System.out.println("num1 == num2: " + (num1 == num2));        System.out.println("num1 != num2: " + (num1 != num2));        System.out.println("num1 <= num2: " + (num1 <= num2));    }}

اقرأ المثال سطرًا سطرًا

  • num1 > num2 تسأل: هل 10 أكبر من 6؟ الجواب true.
  • num1 == num2 تسأل: هل 10 تساوي 6؟ الجواب false.
  • num1 != num2 تسأل: هل 10 تختلف عن 6؟ الجواب true.
  • num1 <= num2 تسأل: هل 10 أصغر من 6 أو تساويها؟ الجواب false.
  • الأقواس حول كل مقارنة مهمة: بدونها يحاول جافا إلصاق النص بالرقم قبل أن يقارن فيحدث خطأ.
جرّباضغط «✎ تعديل» وغيّر num1 إلى 6 ثم شغّل — توقّع أولًا: أي الأسطر الأربعة سيتغير ناتجها؟ (ستنقلب النتائج الأربع كلها: false ثم true ثم false ثم true.)

الجواب الذي يعطيه الحارس ليس للطباعة فقط؛ يمكن أن نحفظه في متغير boolean ونستعمله لاحقًا، تمامًا كما نحفظ رقمًا في int:

StoreAnswer.java
public class StoreAnswer {    public static void main(String[] args) {        int age = 17;        boolean canEnter = age >= 18;        System.out.println("canEnter = " + canEnter);        boolean isSeventeen = age == 17;        System.out.println(isSeventeen);    }}

المتغير canEnter يحمل الجواب false لأن 17 ليست أكبر من أو تساوي 18 — لا دخول اليوم يا صديقي! أما isSeventeen فجوابه true.

فخ المبتدئين: = مقابل ==

علامة = الواحدة ليست سؤالًا. هي أمر: «ضع هذه القيمة في الصندوق». أما السؤال «هل يساوي؟» فيحتاج علامتين ==. الخلط بينهما من أشهر أخطاء بدايات جافا:

= أمر: ضع في الصندوق إسناد (assignment) — يغيّر ما في المتغير age = 18 age 18 الصندوق صار يحمل 18 == سؤال: هل يساوي؟ مقارنة — لا يغيّر شيئًا، فقط يجيب age == 18 true والجواب: نعم
علامة واحدة تضع قيمة في الصندوق، وعلامتان تسألان الصندوق سؤالًا دون أن تغيّر ما فيه
توقّف وفكّرلو كتبنا age = 18 بعلامة واحدة، هل نكون قد سألنا الحارس شيئًا؟
اكشف الإجابة لا — نكون قد وضعنا 18 في age دون أن نسأل. السؤال الحقيقي يحتاج age == 18.

الحارس يعرف الآن أن يسأل سؤالًا واحدًا ويجيب بـ true أو false. في الدرس التالي سيتعلّم أن يجمع سؤالين معًا وأن يقلب جوابه.

اختبر نفسك · +5 نقاط
ما ناتج هذا الكود؟
int age = 17;
boolean canEnter = age >= 18;
System.out.println(canEnter);
اختبر نفسك ٢ · حتى +5 نقاط
أي سطر يخزّن في r جواب السؤال «هل age يساوي 18؟» بشكل صحيح؟
اختبر نفسك ٣ · حتى +5 نقاط
أي قيمة من هذه يمكن أن يحملها متغير من نوع boolean ؟
5·2

بوابة القبول المزدوجة — دمج الشروط

بعد أن تعلّم الحارس أن يسأل سؤالًا واحدًا، قررت إدارة الحرم أن تجعله أذكى: بوابة بحارسين، ولكل حارس رأي.

اليوم لا يكفي أن يسألك الحارس «هل عمرك 18؟». الإدارة تريد أسئلة مركّبة: «هل عمرك 18 و معك البطاقة؟»، أو «هل عمرك 18 أو أكثر أو معك البطاقة؟». لدمج أكثر من سؤال في سؤال واحد تعطينا جافا العوامل المنطقية (logical operators): && («و»، AND) و || («أو»، OR) و ! («ليس»، NOT).

جداول الصدق: AND و OR و NOT

تخيّل بوابة بحارسين، a و b، وكل حارس يجيب بـ true («نعم») أو false («لا»). السؤال: متى تُفتح البوابة؟ الجواب يعتمد على العامل الذي يجمع رأيي الحارسين. جدول الصدق (truth table) يعرض كل الاحتمالات الممكنة ونتيجة كل منها:

«و» (AND) — نربط الحارسين بسؤالَي البوابة الأول:

  • boolean a = age >= 18; ← الحارس الأول: هل عمرك 18 أو أكثر؟
  • boolean b = hasCard; ← الحارس الثاني: هل معك البطاقة؟
a
العمر 18+؟
b
معك البطاقة؟
a && b
يدخل؟
المعنى
truetruetrueعمره 18 ومعه البطاقة ← يدخل
truefalsefalseكبير لكن نسي البطاقة ← يُرفض
falsetruefalseمعه بطاقة لكنه أصغر من 18 ← يُرفض
falsefalsefalseلا هذا ولا ذاك ← يُرفض

و: يجب أن يقول الحارسان نعم.

&& «و»: الاثنان معًا A العمر 18 فأكثر B البطاقة النتيجة true true true true false false false true false false false false
بوابة «و» لا تفتح إلا عند موافقة الحارسين

«أو» (OR) — في يوم المعرض المفتوح خفّفت الإدارة الشرط: يكفي أن تكون 18 أو أكثر أو أن تحمل البطاقة. نفس الحارسين a و b من جدول «و»، فلا نعيد تعريفهما، ونقارن الجدولين صفًّا بصف:

a
العمر 18+؟
b
معك البطاقة؟
a || b
يدخل؟
المعنى
truetruetrueعمره 18 ومعه البطاقة ← يدخل
truefalsetrueكبير بلا بطاقة ← يدخل (يكفي العمر)
falsetruetrueأصغر من 18 لكن معه البطاقة ← يدخل (تكفي البطاقة)
falsefalsefalseلا هذا ولا ذاك ← يُرفض

أو: يكفي حارس واحد.

|| «أو»: يكفي واحد A العمر 18 فأكثر B البطاقة النتيجة true true true true false true false true true false false false
بوابة «أو» لا تُغلق إلا عند رفضهما معًا
قارن الجدولينالصف الأول والأخير متطابقان في الجدولين. الفرق في الصفين الأوسطين فقط: مع && يُرفض من معه شرط واحد فقط، ومع || يدخل.

«ليس» (NOT) — عند مدخل الحرم مكتب صغير: موظفه يخدم فقط من ليس معه بطاقة، ليصدر له واحدة. نكتبها في جافا هكذا:

if (!hasCard) ← «تفضّل، نصدر لك بطاقة»

hasCard
معك البطاقة؟
!hasCard
يحتاج المكتب؟
المعنى
truefalseمعه بطاقة ← لا يحتاج المكتب، يكمل طريقه إلى البوابة
falsetrueبلا بطاقة ← يُوجَّه إلى مكتب إصدار البطاقات

ليس: تعكس القيمة — الصح (true) يصير خطأ (false)، والخطأ يصير صحًّا.

انتبه! لا تغيّر hasCard — البطاقة ما زالت معه أو ليست معه. ! تعطينا جواب السؤال المعاكس فقط.
توقّف وفكّرأحد الحارسين في بوابة && قال «لا» والآخر قال «نعم». هل تُفتح البوابة؟ وماذا لو كانت البوابة ||؟
اكشف الإجابة مع && لا تُفتح (الناتج false) لأن «لا» واحدة تكفي لإفسادها. ومع || تُفتح (الناتج true) لأن «نعم» واحدة تكفي.

والآن بوابة القبول المزدوجة في برنامج حقيقي. الطالب عمره 20 ومعه البطاقة، والمطلوب أن نختبر الحارسين بالعوامل الثلاثة. لاحظ: هذا بالضبط الصف الأول من جدول «و» (a هو age >= 18 وb هو البطاقة، وهي هنا hasCard):

DoubleGate.java
public class DoubleGate {    public static void main(String[] args) {        int age = 20;        boolean hasCard = true;        boolean both = age >= 18 && hasCard;        boolean either = age >= 18 || hasCard;        System.out.println("both = " + both);        System.out.println("either = " + either);        System.out.println("not hasCard = " + (!hasCard));    }}

اقرأ المثال سطرًا سطرًا

  • age >= 18 && hasCard يجمع حارسين: الأول عمر الطالب 20 فيقول «نعم»، والثاني البطاقة موجودة فيقول «نعم». الاثنان وافقا فالناتج true.
  • age >= 18 || hasCard يكفيه حارس واحد وقد وافق الاثنان، فالناتج true أيضًا.
  • (!hasCard) تقلب true إلى false. الأقواس هنا اختيارية، نكتبها لتوضيح القصد.
جرّباضغط «✎ تعديل» وغيّر hasCard إلى false قبل التشغيل. توقّع أولًا: أي الأسطر الثلاثة سيتغير ناتجها وأيها سيبقى؟ (سطر واحد بالضبط يبقى كما هو.)

نعود إلى مثال 5·1: المدى والخارج عنه

في الدرس السابق قارنّا num1 بـ num2 فقط. الآن نستطيع أن نسأل أسئلة مركّبة عن num1 وحدها: هل هو داخل المدى من 1 إلى 20 (حارسان بـ &&)؟ وهل هو خارج المدى من 0 إلى 20 (حارسان بـ ||)؟

ComparisonDemo2.java
public class ComparisonDemo2 {    public static void main(String[] args) {        int num1 = 10;        boolean inRange = (num1 > 0) && (num1 <= 20);        boolean outside = (num1 < 0) || (num1 > 20);        System.out.println("num1 is from 1 to 20: " + inRange);        System.out.println("num1 is outside 0 to 20: " + outside);    }}
  • (num1 > 0) && (num1 <= 20): 10 أكبر من 0 («نعم») و10 لا تزيد عن 20 («نعم»). وافق الحارسان فالناتج true.
  • (num1 < 0) || (num1 > 20): 10 ليست أصغر من 0 («لا») ولا أكبر من 20 («لا»). لم يوافق أي حارس فالناتج false.
جرّباضغط «✎ تعديل» وغيّر num1 إلى 25 ثم شغّل. توقّع أولًا: الحارس num1 <= 20 سيقول «لا» فيصبح السطر الأول false، وفي السطر الثاني سيقول الحارس num1 > 20 «نعم» فيصبح true.

«ليس»: قلب الجواب

أما ! فتعمل على حارس واحد وتعكس جوابه. هذا مثال المعمل: لا نسمح بالدخول إلا لمن معه تصريح:

PermissionDemo.java
public class PermissionDemo {    public static void main(String[] args) {        boolean hasPermission = false;        if (!hasPermission) {            System.out.println("Access denied");        }    }}
  • hasPermission قيمته false، أي لا يوجد تصريح.
  • !hasPermission تعكس القيمة من false إلى true — يعني «ليس معه تصريح» صحيح.
  • الأمر if (شرط، condition) ينفّذ ما بين { } عندما يكون الجواب true، فيُطبع «Access denied». سنتقنه في الدرس 5·3.

فحص المدى: هل الرقم بين حدّين؟

أشهر استعمال لـ && هو أن نسأل: هل الرقم داخل مدى معيّن؟ إدارة الحرم تقبل رقم القاعة من 1 إلى 100 فقط. نحتاج حارسين: الأول «هل الرقم لا يقل عن 1؟» والثاني «هل لا يزيد عن 100؟»، ولا بد من موافقتهما معًا. وهذه هي المسألة الرابعة في المعمل. وفي التحدي الاختياري نضيف حارسًا ثالثًا: المدى من 10 إلى 20 و الرقم زوجي.

RangeCheck.java
public class RangeCheck {    public static void main(String[] args) {        int x = 57;        System.out.println("x is from 1 to 100: " + (x >= 1 && x <= 100));        int n = 14;        System.out.println("n is even and from 10 to 20: " + (n >= 10 && n <= 20 && n % 2 == 0));    }}

تنبّه إلى فخ شائع: الكتابة الرياضية 1 <= x <= 100 لا تصلح في جافا. كل حارس يسأل سؤالًا كاملًا، فنكتب x >= 1 && x <= 100 بحارسين منفصلين. والرقم الزوجي يُعرف بأن باقي قسمته على 2 يساوي صفرًا: n % 2 == 0.

من يُحسب أولًا؟ الأولوية

عند وجود أكثر من عامل منطقي في سطر واحد، تُحسب بهذا الترتيب: ! أولًا، ثم &&، ثم || أخيرًا. أي أن «و» تلتصق بجارتيها أقوى من «أو»، تمامًا كما يسبق الضرب الجمع في الرياضيات. مثلًا true || false && false تُحسب كأنها true || (false && false) أي true || false فتعطي true. والمقارنات (>= و <) تُحسب قبل العوامل المنطقية كلها، لذلك age >= 18 && hasCard لا تحتاج أقواسًا.

نصيحةلا تعتمد على حفظ الأولوية: ضع أقواسًا توضّح قصدك، فمن يقرأ الكود بعدك (وقد يكون أنت بعد أسبوع) سيشكرك.

الحارس يتوقف عند أول رفض: القصر (short-circuit)

الحارسان في بوابة && لا يُسألان دائمًا. جافا تسأل الحارس الأول، فإن قال «لا» فالنتيجة false مهما كان رأي الثاني، ولذلك لا تكلّف نفسها سؤاله. هذا يسمى القصر (short-circuit). وفائدته عملية جدًا: حماية الحارس الثاني من سؤال خطير. المثال هنا متوسط الدرجات: totalScore / assignmentsDone تنهار بقسمة على صفر إذا لم يسلّم الطالب أي واجب. فنضع حارس الأمان أولًا:

assignmentsDone > 0 && totalScore / assignmentsDone > 50 assignmentsDone = 0 , totalScore = 150 الحارس الأول assignmentsDone > 0 ? 0 > 0 = false الحارس الثاني الحارس الثاني totalScore / assignmentsDone > 50 ينتظر دوره لم يُسأل أصلًا فلا قسمة على صفر && false النتيجة النهائية
التعبير فيه حارسان مربوطان بـ &&، والقيمتان هما assignmentsDone = 0 وtotalScore = 150. يبدأ الأول بالسؤال. الحارس الأول يسأل: هل 0 أكبر من 0؟ لا، الجواب false. والحارس الثاني ينتظر دوره. عند أول رفض تتوقف &&: الحارس الثاني لا يُسأل أصلًا، فلا تحدث القسمة على صفر، والنتيجة false.

توقّع: لو رفض الحارس الأول، هل ستحاول جافا مع ذلك تنفيذ totalScore / 0 ؟

توقّف وفكّرلو كتبنا الحارسين بالترتيب المعاكس totalScore / assignmentsDone > 50 && assignmentsDone > 0، ماذا يحدث عندما assignmentsDone = 0 ؟
اكشف الإجابة يُسأل الحارس الذي يقسم أولًا فتقع القسمة على صفر ويتوقف البرنامج بخطأ. الترتيب هو الحماية: حارس الأمان يأتي أولًا.

وهذا هو مثال التحدي في المعمل، المتوسط الآمن، بحارس الأمان في مكانه الصحيح:

SafeAverage.java
public class SafeAverage {    public static void main(String[] args) {        int assignmentsDone = 0;        int totalScore = 150;        boolean passing = assignmentsDone > 0 && totalScore / assignmentsDone > 50;        System.out.println("passing = " + passing);    }}

الناتج passing = false وبلا أي خطأ. جرّب أن تغيّر assignmentsDone إلى 3: ستُسأل الحارسان معًا، والمتوسط 50 ليس أكبر من 50 فيبقى الناتج false أيضًا. وللعامل || قصر مماثل: إن قال الحارس الأول «نعم» فلا حاجة لسؤال الثاني.

اختبر نفسك · +5 نقاط
ما ناتج هذا الكود؟
int x = 150;
System.out.println(x >= 1 && x <= 100);
اختبر نفسك ٢ · حتى +5 نقاط
ما ناتج System.out.println(true || false && false); ؟
اختبر نفسك ٣ · حتى +5 نقاط
أي تعبير يحمي من القسمة على صفر عندما assignmentsDone = 0 ؟
5·3

مكتب الدرجات — القرارات

دخلت الحرم بسلام، والآن تمرّ على مكتب الدرجات حيث يقرر الموظف مصيرك: ناجح، راسب، أو «تفضّل بزيارتنا الأسبوع القادم».

حتى الآن كان البرنامج ينفّذ أسطره كلها من الأول إلى الآخر. لكن موظف المكتب لا يعمل هكذا: ينظر إلى درجتك ثم يختار فرعًا (branch) من الطرق الممكنة. الأمر الذي يمنح البرنامج هذه القدرة اسمه if، وشكله:

  • if (condition) { ... } ينفّذ ما بين القوسين المعقوفين فقط عندما يكون الشرط true، وإلا يتخطاه.
  • if (condition) { ... } else { ... } ينفّذ الفرع الأول إن صح الشرط، والفرع الثاني (else) إن لم يصح. فرع واحد بالضبط يُنفَّذ دائمًا.
RetryOrPass.java
public class RetryOrPass {    public static void main(String[] args) {        int mark = 45;        if (mark >= 50) {            System.out.println("Passed");        } else {            System.out.println("Try again");        }    }}

الدرجة 45 لا تحقق الشرط، فيُنفَّذ فرع else ويُطبع «Try again». غيّر الدرجة إلى 70 وشغّل لترى الفرع الآخر.

الأقواس {} ليست زينة

إذا حذفنا الأقواس المعقوفة، فإن if تتحكم في أول سطر فقط بعدها. أما السطر الثاني فيُنفَّذ دائمًا، سواء صح الشرط أم لا. شاهد ما يحدث لطالب راسب:

BracesTrap.java
public class BracesTrap {    public static void main(String[] args) {        int mark = 40;        if (mark >= 50)            System.out.println("Passed");            System.out.println("Congratulations!");    }}

الدرجة 40 راسبة، ومع ذلك يُطبع «Congratulations!». فالمسافة البادئة لا تعني شيئًا لجافا؛ السطر الثاني ليس داخل الشرط. هذا خطأ يضحك الجميع إلا صاحبه. فاكتب الأقواس دائمًا حتى مع سطر واحد.

سلّم التقديرات: else if

ماذا لو كانت الطرق أكثر من اثنين؟ التقدير له خمسة أحوال: A وB وC وD وF. نسلسل الشروط بـ else if من الأعلى إلى الأسفل، وتُفحص بالترتيب، وأول شرط يصح يُنفَّذ فرعه وحده ثم يخرج البرنامج من السلّم كله. هذه هي الممارسة الأولى في المعمل، GradeMessage:

GradeMessage.java
public class GradeMessage {    public static void main(String[] args) {        int mark = 82;        if (mark >= 90) {            System.out.println("Excellent");            System.out.println("Grade: A");        } else if (mark >= 80) {            System.out.println("Very Good");            System.out.println("Grade: B");        } else if (mark >= 70) {            System.out.println("Good");            System.out.println("Grade: C");        } else if (mark >= 60) {            System.out.println("Pass");            System.out.println("Grade: D");        } else {            System.out.println("Fail");            System.out.println("Grade: F");        }    }}

تابع الدرجة 82 على السلّم خطوة بخطوة، وتوقّع قبل كل ضغطة أين ستقف:

mark = 82 if (mark >= 90) لا: يُتخطّى else if (mark >= 80) نعم: هذا فرعنا else if (mark >= 70) لا يُفحص else if (mark >= 60) لا يُفحص else لا يُنفَّذ Very Good , Grade: B المطبوع
الدرجة mark = 82 تدخل السلّم من أعلاه. الحكم لأول شرط يصح. الشرط الأول: هل 82 >= 90 ؟ لا، الجواب false، فننزل إلى الشرط التالي. الشرط الثاني: هل 82 >= 80 ؟ نعم، الجواب true. وجدنا الفرع الصحيح. يُنفَّذ فرع هذا الشرط وحده، فيُطبع «Very Good» ثم «Grade: B». بقية السلّم تُتخطّى كلها دون فحص، فلا تسأل جافا هل 82 أكبر من 70 رغم أن ذلك صحيح. أول فرع صحيح يُنهي السلّم.

توقّع: الدرجة 82 أكبر من 80 وأكبر من 70 وأكبر من 60 كذلك. فكم فرعًا سيُنفَّذ منها؟

توقّف وفكّرلو قلبنا ترتيب السلّم فبدأنا بـ mark >= 60 ثم mark >= 90، فماذا يطبع للدرجة 95؟
اكشف الإجابة يطبع «Grade: D»! لأن 95 تحقق الشرط الأول (أكبر من 60) فيُنفَّذ فرعه ويخرج السلّم قبل أن يصل إلى 90. الترتيب في السلّم يبدأ من الأصعب إلى الأسهل.

if داخل if: العمر ثم البطاقة

أحيانًا يكون القرار على مرحلتين: عند باب الكلية يسألك الحارس أولًا عن العمر، وفقط إذا نجحت يسأل عن البطاقة. نضع if داخل فرع if آخر وهذا يسمى الشرط المتداخل (nested if). هذه هي الممارسة الثانية في المعمل:

GateNested.java
public class GateNested {    public static void main(String[] args) {        int age = 20;        boolean hasCard = true;        if (age >= 18) {            if (hasCard) {                System.out.println("Welcome in");            } else {                System.out.println("Show your ID card");            }        } else {            System.out.println("Too young");        }    }}
لا نعم لا نعم age >= 18 hasCard Too young Show your ID card Welcome in المسار الأخضر: طالب عمره 20 ومعه البطاقة
شجرة القرار: السؤال الأول يفرز بالعمر، والثاني لا يُطرح إلا لمن نجح في الأول
توقّف وفكّرلو كان عمر الطالب 16 ومعه البطاقة، هل يُسأل عن البطاقة أصلًا؟
اكشف الإجابة لا. شرط العمر يرفضه فيذهب مباشرة إلى فرع else الخارجي ويُطبع «Too young»، ولا يصل إلى سؤال البطاقة. السؤال الداخلي لا يُطرح إلا عند نجاح الخارجي.

الاختصار: العامل الشرطي ? :

عندما يكون القرار بين قيمتين فقط، تمنحنا جافا اختصارًا اسمه العامل الشرطي (ternary operator) وصيغته condition ? valueIfTrue : valueIfFalse. يُقرأ هكذا: «هل الشرط صحيح؟ خذ القيمة الأولى، وإلا فالثانية». هذه هي الممارسة الثالثة في المعمل، أكبر عددين:

MaxDemo.java
public class MaxDemo {    public static void main(String[] args) {        int n1 = 12;        int n2 = 7;        int max = (n1 > n2) ? n1 : n2;        System.out.println("Maximum = " + max);    }}

الشرط n1 > n2 يعني هل 12 أكبر من 7؟ نعم، فتؤخذ القيمة الأولى n1 وتُحفظ في max، فيُطبع Maximum = 12. وهو يعادل تمامًا if-else يضع في max إحدى القيمتين، لكن في سطر واحد.

تطبيق من الكافتيريا: خصم الطلبات الكبيرة

الكافتيريا تعطي خصم 10% لمن تبلغ فاتورته 200 أو أكثر. هذا قرار if بلا else؛ نبدأ بأن المبلغ المدفوع هو الفاتورة نفسها، ثم نعدّله إذا تحقق الشرط:

Cafeteria.java
public class Cafeteria {    public static void main(String[] args) {        int total = 240;        int pay = total;        if (total >= 200) {            pay = total - total / 10;        }        System.out.println("You pay: " + pay);    }}

الفاتورة 240 تتجاوز حد الخصم، فيُخصم عُشرها (24) ويُطبع You pay: 216. جرّب أن تجعل total يساوي 150 فلن يدخل البرنامج فرع الخصم ويبقى المطبوع You pay: 150. نفس الفكرة تحل مسائل المعمل الأولى: هل العدد موجب أم سالب أم صفر؟ وهل هو زوجي أم فردي؟

اختبر نفسك · +5 نقاط
ما ناتج هذا الكود؟
int n = 0;
if (n > 0) {
    System.out.println("Positive");
} else if (n < 0) {
    System.out.println("Negative");
} else {
    System.out.println("Zero");
}
اختبر نفسك ٢ · حتى +5 نقاط
ما ناتج الدرجة 95 على سلّم يبدأ بالكود التالي؟
if (mark >= 60) {
    System.out.println("Grade: D");
} else if (mark >= 90) {
    System.out.println("Grade: A");
}
اختبر نفسك ٣ · حتى +5 نقاط
ما ناتج هذا الكود؟
int a = 3;
int b = 8;
System.out.println(a > b ? a : b);
5·4

لوحة المصعد — switch

وصلت إلى مبنى الكلية فوجدت مصعدًا بلوحة أزرار: تضغط رقم الطابق فيذهب إليه مباشرة دون أسئلة كثيرة.

بعد البوابة ومكتب الدرجات صعدت إلى مبنى الكلية. المصعد أمامك بلوحة أزرار: تضغط 3 فيذهب إلى الطابق الثالث، وتضغط 1 فيذهب إلى الأرضي. المصعد لا يسأل «هل الرقم أكبر من كذا؟»؛ يأخذ الرقم ويقفز إلى الطابق المناسب مباشرة.

كان يمكننا كتابة ذلك بسلسلة if / else if من الدرس 5·3، لكن حين نقارن قيمة واحدة بعدة قيم ثابتة تقدّم لنا جافا أداة أنظف هي تعليمة switch (التبديل). تأخذ التعليمة القيمة، وتبحث بين حالات (case) عن الحالة المطابقة، ثم تنفّذ ما فيها.

طرفة من الحرمطلاب كلية الحاسب يقولون إن المصعد الوحيد الذي لا يتعطل هو الذي يستخدم switch، لأنه يعرف طابقه بالضبط ولا يفكر كثيرًا.

مثال كامل للوحة المصعد، حيث يطبع كل رقم اسم الطابق المقابل له:

LiftPanel.java
public class LiftPanel {    public static void main(String[] args) {        int floor = 3;        switch (floor) {            case 1:                System.out.println("Floor 1: Library");                break;            case 2:                System.out.println("Floor 2: Cafeteria");                break;            case 3:                System.out.println("Floor 3: Lecture halls");                break;            default:                System.out.println("There is no such floor");        }    }}

اقرأ المثال سطرًا سطرًا

  • switch (floor) تأخذ قيمة المتغير floor وتقارنها بالحالات بالترتيب. القيمة هنا 3 فتبدأ من case 3:.
  • كل case يعني «إذا كانت القيمة تساوي هذا الرقم فابدأ التنفيذ من هنا». الرقم يُتبع بنقطتين رأسيتين :.
  • break تعني «اخرج من تعليمة switch الآن». بدونها يتابع التنفيذ إلى الحالات التالية، وسنرى ذلك بعد قليل.
  • default (الحالة الافتراضية) تُنفَّذ حين لا تطابق القيمة أي case، مثل الضغط على طابق غير موجود. مع القيمة 9 سيطبع البرنامج رسالة الطابق غير الموجود.
جرّباضغط «✎ تعديل» وغيّر floor إلى 9 ثم شغّل: أي سطر سيطبع قبل أن تجرب؟

مع char وتجميع الحالات

switch لا تقتصر على int؛ تعمل أيضًا مع char ومع String. وحين تشترك أكثر من قيمة في الفرع نفسه نكتب حالاتها واحدة تحت الأخرى دون break بينها (تجميع الحالات):

GradeSwitch.java
public class GradeSwitch {    public static void main(String[] args) {        char grade = 'B';        switch (grade) {            case 'A':            case 'B':                System.out.println("Well done");                break;            case 'C':                System.out.println("Pass");                break;            default:                System.out.println("See your instructor");        }    }}

الحرفان A وB يشتركان في الفرع نفسه (الطباعة نفسها): عند B يبدأ التنفيذ من case 'B': وينزلق إلى السطر الذي يطبع Well done، ثم يخرج break. هنا الانزلاق مقصود ومفيد.

مع String

عند استخدام String تقارن switch النصوص بمعنى equals، أي حسب محتوى النص لا حسب موقعه في الذاكرة. وإذا لم يطابق النص أي حالة ولا يوجد default فلن يُنفَّذ شيء.

ServiceDesk.java
public class ServiceDesk {    public static void main(String[] args) {        String service = "print";        switch (service) {            case "print":                System.out.println("Printing office: floor 1");                break;            case "library":                System.out.println("Library: floor 2");                break;            default:                System.out.println("Unknown service");        }    }}
انتبهالنص يجب أن يطابق الحالة تمامًا بما فيها حالة الأحرف: "Print" تختلف عن "print".

فخ نسيان break

أشهر خطأ في switch هو نسيان break. حين تجد جافا الحالة المطابقة تبدأ التنفيذ منها، ثم تكمل إلى كل الحالات التي تحتها دون أن تفحصها، حتى تصل إلى break أو إلى نهاية التعليمة. هذا هو السقوط عبر الحالات (fall-through)، كمصعد يمر على كل الطوابق التالية بدل أن يتوقف:

LiftTrap.java
public class LiftTrap {    public static void main(String[] args) {        int floor = 2;        switch (floor) {            case 1:                System.out.println("Floor 1");            case 2:                System.out.println("Floor 2");            case 3:                System.out.println("Floor 3");            case 4:                System.out.println("Floor 4");        }    }}

تتبّع التنفيذ خطوة بخطوة عند اختيار الطابق 2:

switch (floor) { floor = 2 case 1: println("Floor 1"); case 2: println("Floor 2"); case 3: println("Floor 3"); case 4: println("Floor 4"); الناتج Floor 2 Floor 3 Floor 4
اخترنا الطابق 2: تقفز جافا مباشرة إلى case 2: وتتجاوز case 1: دون تنفيذها. تطبع Floor 2. لا يوجد break هنا، فلا شيء يوقف التنفيذ. ينزلق التنفيذ إلى الحالة التالية دون فحص جديد (السقوط عبر الحالات، fall-through) فتطبع Floor 3. لم تُفحص القيمة 3 أصلًا: دخلنا case 3: بالانزلاق فقط. ويستمر إلى case 4: فتطبع Floor 4، ولا يتوقف إلا عند نهاية switch أو عند أول break.

توقّع: ماذا ستطبع لوحة المصعد عند اختيار الطابق 2؟

هل فُحص الرقم 3 هنا، أم دخلنا الحالة بالانزلاق فقط؟

الحل أن نضع break في نهاية كل حالة نريد أن تتوقف عندها:

LiftFixed.java
public class LiftFixed {    public static void main(String[] args) {        int floor = 2;        switch (floor) {            case 1:                System.out.println("Floor 1");                break;            case 2:                System.out.println("Floor 2");                break;            case 3:                System.out.println("Floor 3");                break;            case 4:                System.out.println("Floor 4");                break;        }    }}
قاعدة ذهبيةاكتب break في آخر كل case إلا إذا كنت تريد الانزلاق عمدًا، كما في تجميع الحالات. ومن الجيد أن تتفحص كل حالة لديك بسؤال: هل أريد أن يكمل إلى التي بعدها؟

switch أم if؟

الاختيار بينهما يعتمد على شكل السؤال:

الحالةالأنسبمثال
قيمة واحدة تُقارن بقيم ثابتةswitchرقم الطابق، رقم الشهر، خيار القائمة
نطاق من القيمif / else ifالدرجة 90 فأكثر، العمر بين 18 و60
شرط مركّبifالعمر 18 فأكثر والبطاقة موجودة

أمثلة من المعمل

المثال الأول من المعمل (Lab 5·2، التمرين 4): برنامج يقرأ رقم شهر من 1 إلى 4 ويطبع اسمه. إذا أدخلت 3 يطبع March، وأي رقم آخر يطبع رسالة الخطأ من default:

MonthName.java
import java.util.Scanner;public class MonthName {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        System.out.println("Enter a month number (1-4):");        int month = sc.nextInt();        switch (month) {            case 1:                System.out.println("January");                break;            case 2:                System.out.println("February");                break;            case 3:                System.out.println("March");                break;            case 4:                System.out.println("April");                break;            default:                System.out.println("Invalid month");        }    }}

والمثال الثاني (التمرين 5): حاسبة صغيرة تقرأ رقمين ثم اختيار العملية: 1 للجمع و2 للطرح، وأي اختيار آخر يعطي Invalid choice:

SimpleCalc.java
import java.util.Scanner;public class SimpleCalc {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        System.out.println("Enter two numbers:");        int a = sc.nextInt();        int b = sc.nextInt();        System.out.println("Choose 1 to add, 2 to subtract:");        int choice = sc.nextInt();        switch (choice) {            case 1:                System.out.println(a + " + " + b + " = " + (a + b));                break;            case 2:                System.out.println(a + " - " + b + " = " + (a - b));                break;            default:                System.out.println("Invalid choice");        }    }}
ملاحظة ختامية: الشكل الحديثفي الإصدارات الأحدث من جافا يمكن كتابة الحالة بسهم case 1 -> فتُنفَّذ الحالة المطابقة فقط ولا نحتاج break ولا يحدث سقوط. هذا الشكل مختصر، لكن الشكل الكلاسيكي بنقطتين هو الذي سنستعمله في مختبراتنا. جرّب الشكل الحديث هنا:
ArrowLift.java
public class ArrowLift {    public static void main(String[] args) {        int floor = 2;        switch (floor) {            case 1 -> System.out.println("Floor 1: Library");            case 2 -> System.out.println("Floor 2: Cafeteria");            case 3 -> System.out.println("Floor 3: Lecture halls");            default -> System.out.println("There is no such floor");        }    }}

تلخيص الدرس

  • switch تقارن قيمة واحدة (int أو char أو String) بعدة حالات case ثابتة.
  • break تخرج من switch، ونسيانها يسبب السقوط عبر الحالات (fall-through).
  • default تلتقط كل قيمة لا تطابق حالة، وتجميع الحالات يجعل عدة قيم تشترك في فرع واحد.
  • للنطاقات والشروط المركبة نستعمل if، ولاختيار قيمة من قائمة ثابتة نستعمل switch.
اختبر نفسك · +5 نقاط
ما ناتج هذا الكود؟
int x = 2;
switch (x) {
    case 1:
        System.out.print("A");
    case 2:
        System.out.print("B");
    case 3:
        System.out.print("C");
        break;
    case 4:
        System.out.print("D");
}
اختبر نفسك ٢ · حتى +5 نقاط
ما ناتج هذا الكود؟
char g = 'C';
switch (g) {
    case 'A':
    case 'B':
        System.out.println("Great");
        break;
    case 'C':
    case 'D':
        System.out.println("Pass");
        break;
    default:
        System.out.println("Retry");
}
اختبر نفسك ٣ · حتى +5 نقاط
أي مهمة من هذه هي الأنسب لتعليمة switch ؟
★

تحدي: أين الخطأ؟

ثلاثة برامج عن القرارات: أحدها لا يُترجم أصلًا، والثاني يعمل لكنه يطبع شيئًا غير منطقي، والثالث يسقط عبر الحالات. اقرأ الكود وتتبّعه ثم اضغط على السطر المخالف.

المقطع ١: نريد أن يسأل البرنامج هل x يساوي 5، لكنه يرفض الترجمة برسالة incompatible types. أين الخطأ؟

public class Trap1 { public static void main(String[] args) { int x = 3; if (x = 5) { System.out.println("x is five"); } } }

الشرط يجب أن يعطي true أو false. ما نوع القيمة التي تنتجها x = 5؟ وما الفرق بين علامة واحدة وعلامتين (الدرس 5·1)؟

المقطع ٢: الدرجة 45 راسبة، ومع ذلك يطبع البرنامج Passed. أين الخطأ؟

public class Trap2 { public static void main(String[] args) { int mark = 45; if (mark >= 60); System.out.println("Passed"); } }

ماذا يعني وجود ; مباشرة بعد قوس الشرط؟ هل بقي للشرط جسم يتحكم في سطر الطباعة (الدرس 5·3)؟

المقطع ٣: ضغطنا الطابق 1 فطبع المصعد Floor 1: Library ثم Floor 2: Cafeteria أيضًا! أين الخطأ؟

public class Trap3 { public static void main(String[] args) { int floor = 1; switch (floor) { case 1: System.out.println("Floor 1: Library"); case 2: System.out.println("Floor 2: Cafeteria"); break; } } }

عند مطابقة case 1: أين يتوقف التنفيذ؟ ما الذي ينقصه ليخرج من switch قبل الحالة التالية (فخ السقوط في الدرس 5·4)؟

لماذا هذا التحدي مهم؟هذه الأخطاء الثلاثة من أكثر ما يقع فيه المبتدئ في القرارات: إسناد بدل مقارنة، وفاصلة منقوطة بعد if، ونسيان break. انسخ كل مقطع إلى المحرر وصحّحه وشغّله.
✎

تمارين الوحدة ٥

تمارين على القرارات في جافا (المقارنة، وif، وelse if، والمنطق، وswitch). كل تمرين أمامه كود ناقص: اضغط «✎ تعديل»، اكتب الكود المطلوب مكان التعليقات، اضغط «✔ حفظ» ثم «▶ تشغيل» وقارن النتيجة بالهدف. أدخل القيم الاختبارية المكتوبة في كل تمرين حتى يطابق إخراجك الهدف.

اقرأ عددًا صحيحًا بـ nextInt() ثم اطبع Positive إذا كان أكبر من 0، وNegative إذا كان أصغر من 0، وZero إذا كان يساوي 0. القيمة الاختبارية: أدخل «-7».

SignCheck.java
import java.util.Scanner;public class SignCheck {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        // ١) اطبع (بـ println في سطر مستقل) "Enter a number:" ثم اقرأ العدد في n بـ nextInt        // ٢) if: إذا كان n أكبر من 0 فاطبع Positive،        //    else if: إذا كان أصغر من 0 فاطبع Negative،        //    else: فاطبع Zero    }}
الهدف — بالقيمة الاختبارية -7
Enter a number:
Negative
تلميحالسؤال الأول n > 0 والثاني n < 0، وما بقي بعدهما هو الصفر، فيكفي else الأخيرة دون شرط.

اقرأ إجمالي فاتورة الكافتيريا (عددًا صحيحًا). إذا كان 200 فأكثر اطبع Discount available وإلا اطبع No discount. القيمة الاختبارية: أدخل «250».

Discount.java
import java.util.Scanner;public class Discount {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        // ١) اطبع (بـ println في سطر مستقل) "Enter the bill total:" ثم اقرأ المبلغ في total بـ nextInt        // ٢) if: إذا كان total أكبر من أو يساوي 200 فاطبع Discount available        //    else: فاطبع No discount    }}
الهدف — بالقيمة الاختبارية 250
Enter the bill total:
Discount available
تلميحالشرط total >= 200 يشمل 200 نفسها لأن العامل >= يعني أكبر من أو يساوي.

اقرأ درجة الطالب وحوّلها إلى تقدير: 90 فأكثر A، ثم 80 فأكثر B، ثم 70 فأكثر C، ثم 60 فأكثر D، وما دون ذلك F، واطبع بالشكل Grade: B. القيمة الاختبارية: أدخل «83».

GradeCalc.java
import java.util.Scanner;public class GradeCalc {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        // ١) اطبع (بـ println في سطر مستقل) "Enter your mark:" ثم اقرأ الدرجة في mark بـ nextInt        // ٢) استعمل سلسلة if / else if:        //    90 فأكثر A، ثم 80 B، ثم 70 C، ثم 60 D، وما عدا ذلك F        //    واطبع بالشكل: Grade: B    }}
الهدف — بالقيمة الاختبارية 83
Enter your mark:
Grade: B
تلميحرتّب الأسئلة من الأعلى إلى الأدنى: الدرجة 83 تفشل في mark >= 90 فتنتقل إلى mark >= 80 فتنجح، ولا تُفحص بقية الفروع.

البوابة تقرأ العمر وهل معك بطاقة (1 نعم، 0 لا). إذا كان العمر 18 فأكثر و البطاقة موجودة تُفتح وتعرض قائمة الوجهات لتقرأ اختيارك وتنفّذ switch. وفي else if (تحقق شرط واحد فقط: أحدهما أو الآخر) تطبع Visitor pass needed، وفي else تطبع Gate closed. القيم الاختبارية: أدخل «20» ثم «1» ثم «2».

SmartGate.java
import java.util.Scanner;public class SmartGate {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        // ١) اطبع (بـ println في سطر مستقل) "Enter your age:" ثم اقرأ العمر في age        // ٢) اطبع (بـ println في سطر مستقل) "Enter 1 if you have a card, 0 if not:" ثم اقرأ card        // ٣) if: إذا كان age أكبر من أو يساوي 18 و card يساوي 1 (&&):        //      اطبع (بـ println) Gate open ثم اطبع (بـ println) "Where to? 1 Library, 2 Lab, 3 Cafeteria"        //      واقرأ place، ثم switch: 1 يطبع Going to Library،        //      و2 يطبع Going to Lab، و3 يطبع Going to Cafeteria،        //      وغير ذلك يطبع Unknown destination        //    else if: إذا كان واحد من الشرطين فقط محققًا (||): اطبع Visitor pass needed        //    else: اطبع Gate closed    }}
الهدف — بالقيم الاختبارية 20 و1 و2
Enter your age:
Enter 1 if you have a card, 0 if not:
Gate open
Where to? 1 Library, 2 Lab, 3 Cafeteria
Going to Lab
تلميحاجمع الشرطين بـ && في if الأولى، وضع switch داخل جسمها، وللفرع الثاني استعمل || لأن تحقق واحد منهما يكفي.