10 minute read

משתנים הם מהכלים השימושיים ביותר ברוב שפות התכנות. למעשה, אולי יותר נכון לומר שברוב שפות התכנות הם מהדברים הבסיסיים ביותר בשפה, שבלעדיהן כמעט אי אפשר לכתוב אפילו את התוכנות הקטנות ביותר. משתנים מאפשרים לנו לעבוד עם נתונים ולעבוד איתם. “נתונים” אולי נשמע כמו משהו שרק מתכנתים שעוסקים בסטטיסטיקות צריכים לעבוד איתו, אבל “נתונים” זה דבר הרבה יותר רחב - “נתונים” עבורנו זה גם “האם הנהגת בדיוק לחצה על הכפתור בג’ויסטיק שלה שאמור לגרום לרובוט לבלום”, או “מה המהירות הנוכחית של הרובוט”, “מה המצלמה רואה” ו”איזו מהירות לשלוח למנוע של מערכת הירייה”, וכל מיני דברים נוספים.

לא כל “נתון” יקבל אצלנו “משתנה”, אבל משתנים הם דרך חשובה לעבוד עם נתונים. למשתנים ב-Java יש שם וטיפוס. נתחיל מהשם.

שמות של משתנים

הקומפיילר של Java מחייב אותנו לכללים מסוימים לשמות של משתנים. כבר הזכרתי את החוקים האלה כאן בעבר בהקשר אחר, כי הם נכונים גם לכל הדברים שנקראים “שמות” ב-Java, אבל הנה הם שוב:

  • שמות יכולים לכלול רק: אותיות (קטנות וגדולות), קו תחתון (_), סימן הדולר ($) וספרות
    • שימו לב שזה אומר שאסורים רווחים ( )
    • בתאוריה מותרות גם אותיות שלא באנגלית כמו למשל יוונית או עברית אבל יש להמנע מזה
  • אסור להתחיל שם בספרה
  • ישנה רשימת מילים שמורות (reserved keywords) שיש להן משמעות מיוחדת ב-Java ואי אפשר להשתמש בהן כשמות ב-Java. המילה package היא אחת מהן, כמו כן מילים נוספות שראינו בפוסט הזה: public, class, static, void. הרשימה המלאה (והלא מאוד ארוכה) כאן, באתר הרשמי של Java.

בנוסף, יש כללים לא מחייבים בשם קונבנציות (מוסכמות), והם:

  • שמות צריכים להיות משמעותיים
  • שמות של משתנים מתחילים באותיות קטנות. אם יש יותר ממילה אחת, אז כל מילה למעט הראשונה מתחילה באות גדולה. שאר האותיות קטנות. אני מדגיש - גם כשיש יותר ממילה אחת וגם כשלא, המילה הראשונה מתחילה באות קטנה. סגנון זה נקרא camelCase, כי האותיות הגדולות שמוקופות באותיות קטנות נראות כמו דבשות של גמל.

יש לי עמוד שאני מרכז בו קונבנציות ב-Java.

טיפוסים של משתנים

Java היא שפה שבה למשתנים יש טיפוסים. טיפוס (type) הוא סוג המידע שיכול להיות במשתנה. יש למשל טיפוס בשם int שבו ניתן לאחסן מספרים שלמים (גם חיוביים, גם אפס וגם שליליים - עד גודל מסוים). ויש טיפוס בשם double שבו ניתן לאחסן מספרים שאינם בהכרח שלמים (עד רמת דיוק מסוימת). ויש גם עוד.

כמה טיפוסים בסיסיים ב-Java

שם הטיפוס משמעות ערכים אפשריים הערות
int מספרים שלמים 0, -1, 5, 4319  
double מספרים עשרוניים 0.0, -0.1,-9876.7, 123.54 שימו לב שגם כשהמספר שלם אני כותב אותו עם “נקודה 0” כדי להראות שהוא ב”צורת” double ולא ב”צורת” int. למספר 0.0 יש אותו ערך כמו ל-0 אבל “צורה” שונה.
char תו (אחד) של טקסט 'a', 'A', ' ', '+', 'א', 'α', 'ﷺ', 'ﭏ',’'🗾' שימו לב שאני מייצג כל תו מוקף בגרש יחיד משני הצדדים
boolean ערך אמת – “כן”/”לא” true, false  
long מספרים שלמים (בטווח יותר גדול) 0L, 1234L, 5534023222112865483L, -5270498306774157604L שימו לב שאני מסיים את המספר ב-L כדי להבדיל אותו מ-int. כמו ההבדל בין int ל-double, למספר 1L יש אותו ערך כמו ל-1 אבל “צורה” שונה.
String טקסט – רצף של תווים “”, “Hello world” שימו לב שהטקסט מוקף במרכאות משני הצדדים (אין להתבלבל בין מרכאות - מה שנקרא באנגלית double quotes, לבין גרשיים, מה שנקרא באנגלית single quotes)

אנלוגיה למשתנים

אני משתמש בצעצוע הילדים הזה המוכר לכולנו (אני מקווה) כאנלוגיה למשתנים:

מזהים? ילדים אמורים להתרגל לקונספט של צורות על ידי כך שהם מוציאים ומכניסים צורות שונות לחורים השונים ורואים מה מסתדר. ובכן, באנלוגיה הזו, הטיפוס הוא כמו צורה, למשל נדמיין int בתור צורת עיגול ו-char בתור צורת ריבוע. משתנים הם כמו החורים בצעצוע בצורות השונות. לחור בצורת עיגול ניתן להכניס גלילים שונים, ועל הגלילים השונים שאפשר להכניס לחור הזה אפשר לחשוב בתור ערכים שונים מאותה צורה. בצעצועים האלה בדרך כלל יש רק צורה אחת לכל חור, אבל אף אנלוגיה אינה מושלמת.

כעת, כשאנחנו יודעים על לחשוב על משתנים, אפשר לראות איך משתמשים בהם.

שימוש במשתנים

הצהרה

ראשית, כדי שיהיה לנו משתנה עם טיפוס מסוים ועם שם מסוים, צריך להצהיר עליו. הצהרה על משתנה היא מה שאומר ל-Java “אני רוצה שיהיה לי משתנה עם השם הזה ועם הטיפוס הזה”. המבנה של פקודת הצהרה היא:

<typename> <varname>;

כאשר <typename> מציין את שם הטיפוס ו-<varname> מציין את שם המשתנה (הסימנים >, < הם לא חלק מהקוד - אני משתמש בהם כדי לציין “הנה אלמנט בקוד”). לדוגמה, השורה הבאה מצהירה על משתנה בשם price מסוג double:

double price;

שימו לב שהצהרה מסתיימת בנקודה-פסיק. זה כי מבחינתנו זוהי פקודה, וכמו שדיברנו בפוסט על פקודות פלט, פקודות מסתיימות בנקודה-פסיק.

השמה

לאחר שמשתנה מוגדר, ניתן להכניס ערכים לתוכו - זה נקרא השמה. השמה היא סוג של פקודה, והמבנה שלה הוא כזה:

<varname> = <value>;

כאשר <varname> הוא שם המשתנה ו-<value> הוא הערך אותו אנחנו רוצים להכניס למשתנה. אם המשתנה הוא למשל מטיפוס char (תו יחיד של טקסט), אז אפשר ש-<value> יהיה למשל 'a' או ' '; אם המשתנה הוא מטיפוס int, אפשר להכניס לתוכו מספר שלם (שאינו גדול מדי ל-int), או חישוב שהתוצאה שלו היא מספר שלם, כמו 1 + 2 * 9.

לדוגמא, בקוד הבא אני מכריז על שני משתנים, ומשים לשניהם ערכים:

int teamNumber;
teamNumber = (613 + 4) * 7;
char myNameInitial;
myNameInitial = 'Y';

כדאי להבהיר שלמרות שמופיע פה הסימן =, זו לא משוואה מתמטית. בשפת Java, המשמעות של:

x = 5;

אינה המשוואה $x = 5$, אלא הפקודה “הכנס את הערך 5 לתוך המשתנה 5”. אפשר לחשוב על ה-= כאילו למרות שמדובר בסימן של שוויון מתמטי, המשמעות היא “חץ שמאלה”, כלומר לדמיין שאנחנו רואים בעצם את השורה הבאה:

x <- 5;

חשוב לציין - בJava זה לא יעבוד - זו רק צורה לדמיין את זה. למרות שיש שפות שבהן משתמשים בסימון הזה במקום בסימן = להשמה למשתנים (למשל שפת R), שפת Java היא לא אחת מהן. בכלל, ברוב השפות שבשימוש היום, הסימון הוא כמו של Java (לא ש-Java הייתה הראשונה שעשתה זאת).

בקיצור, פשוט צריך להתרגל לזה שב-Java, השורה ;x = 5 נראית כמו משוואה, אבל היא לא, היא פקודת השמה. מתרגלים לזה די מהר.

השמה שגויה לא מתקמפלת

יש סיבה לכך ש-Java מכריחה אותנו להצמיד טיפוסים למשתנים, והיא - Java יכולה למנוע מאיתנו לעשות השמות לא הגיוניות. לערך שמשימים למשתנה יש טיפוס משל עצמו, וכאמור, הוא צריך להתאים לצורה של המשתנה. ל-276.5 * 8 יש צורה של double, ולכן, אי אפשר להכניס אותו ל-int. נסיון לעשות זאת יהווה שגיאת קומפילציה:

int number;
number = 8 * 276.5; // ❎ does not compile

שפת Java תסמן לנו את השורה הבעייתית בקו אדום ותגיד שההוראה הזו לא הגיונית. מבחינת Java, הפקודה הזו הגיונית באותה מידה כמו שהמשפט “קח את בקבוק הבושם הזה ותשתמש בו בתור רמזור” הגיונית לבן-אדם, כלומר: בכלל לא. Java לא מבינה מה אנחנו רוצים ממנה. וזה דבר טוב. שגיאות קומפילציה הן מנגנון בטיחות שימושי מאוד. אי אפשר להפוך את התוכנית הזו למשהו ניתן-להרצה לפני שנטפל בטעות ההגיון הזו. המשתנה number נועד לדברים עם צורה של מספר שלם.

תחביר מקוצר - הצהרה והשמה

כמעט תמיד נרצה לבצע את ההשמה הראשונה למשתנה מיד אחרי הצהרתו, כמו בדוגמאות בקוד למעלה. לצורך כך, יש תחביר מקוצר שעושה את שני הדברים האלה בשורה אחת:

<typename> <varname> = <value>;

למשל את הקוד למעלה אפשר לקצר לשתי שורות:

int teamNumber = (613 + 4) * 7;
char myNameInitial = 'Y';
Java לדוברות פייתון

חלק מהתלמידות שלי מגיעות עם רקע של פייתון. מדי פעם אני משווה בין Java לפייתון כדי לעזור להן להבין את Java. חלונית זו רלוונטית רק למי שמגיעה מרקע של פייתון וניתן לפתוח ולסגור אותה בעזרת המשולש בצד.


יש שני הבדלים עיקריים בין פייתון ל-Java כשמדובר במשתנים. האחת, היא שבפייתון לא מצהירים על משתנים. ברגע שעושים פקודת השמה, המשתנה קיים. מיד לאחר השורה:

team_number = 4319

המשתנה team_number קיים וערכו 4319. בנוסף, בפייתון לא מצמידים טיפוסים למשתנים, וכתוצאה, אפשר להחליף את הערך של משתנה בין טיפוסים שונים:

team_number = 1  # now it's a whole number
team_number = "hello world"  # now it's a string (text)

החל מפייתון 3, יש מנגנון מסוים של טיפוסים למשתנים בפייתון, אבל הוא אופציונלי, והוא לא מעניין אותנו (בעיקר כי אנחנו עוסקים ב-Java; אני מציג פייתון רק כהשוואה למי שמגיעה מפייתון, ולרוב כשלומדים פייתון במסגרות תיכוניות לא מלמדים את המנגנון הזה, שהוא גם חדש יחסית. מי שבכל זאת מתעניינת יכולה לקרוא על זה כאן).

קריאה ממשתנה

בפלט

למדנו איך להשים (לכתוב) למשתנה, אבל יכולת לכתוב לא מאוד עוזרת בלי יכולת לקרוא: לאחר שהכנסנו ערך למשתנה, אנחנו רוצים גם להשתמש בערך הזה. דרך אחת להשתמש בערך של משתנה הוא בפלט. ראינו איך לעשות פלט של טקסט, אך כעת, אנחנו יכולים להשתמש גם במשתנים בפלט - אם משתנה מופיע בתוך System.out.print או System.out.println, אז Java בזמן הריצה תדפיס למסך את הערך של המשתנה:

int answer = 3 * 7 * (1 + 1);
System.out.println(answer);

כאשר נריץ את התוכנית הזו, יודפס למסך המספר 42.

בהשמה

אפשר להשים למשתנה אחד ערך של משתנה אחר:

x = y;

המשתנה x מעתיק את הערך שיש בתוך המשתנה y. כעת, ניתן לשנות את המשתנה y והערך הקודם של y עדיין יהיה זמין לנו במשתנה x (כל עוד לא נשים משהו אחר לתוך x).

אפשר גם להשים את הערך של משתנה לתוך עצמו, אבל זה לא יעשה שום דבר משמעותי. שורה כזו היא תקינה ב-Java, הקומפיילר יסמן אותה לא כשגיאה, אלא כאזהרה - כלומר, הקומפיילר של Java יגיד לנו “אני מסוגל להפוך את זה לקוד בר הרצה, אז אני אעשה את זה, אבל לרוב לא עושים דברים כאלה, יכול להיות שהתכוונת למשהו אחר?”. נסו את זה - כתבו פקודה של השמה של הערך של משתנה לתוך עצמו, ותראו ש-Java מסכימה לקמפל ולהריץ את הקוד, אבל מסמנת לכם קו צהוב מתחת לשורה הזו (במקום קו אדום שמסומן מתחת לשורות שגורמות לשגיאות קומפילציה).

שאלה למחשבה: בהינתן שני משתנים, x, y, איך ניתן להחליף בין הערכים שלהם, כך שהערך שהיה ב-x יהיה ב-y ולהפך?

בחישוב

אפשר לעשות חישובים שכוללים משתנים, ובתוצאה להשתמש בפלט, או בהשמה:

System.out.println(x * x);
int cubed = x * x * x;

סימונים מקוצרים

לעתים קרובות נרצה להשים לתוך משתנה מספרי ערך שתלוי במשתנה עצמו, כלומר, לעדכן את המשתנה. לפעמים נרצה למשל להוסיף ביטוי מסוים למשתנה, כלומר:

var = var + <some expression>;

שפת Java מספקת לנו תחביר מקוצר לכך:

var += <some expression>;

שימו לב שמדובר בתחביר מקוצר - לא נוספה לנו יכולת חדשה בזה, אלא רק נוצרה דרך יותר קצרה ואולי גם יותר קריאה לכתוב משהו שכבר יכולנו לעשות.

בדומה, יש תחביר מקוצר לחסר, לכפול או לחלק משתנה בביטוי מסוים:

var -= <some expression>; // short for:    var = var - <some expression>;
var *= <some expression>; // short for:    var = var * <some expression>;
var /= <some expression>; // short for:    var = var / <some expression>;

ויש גם תחביר מקוצר לפעולה נוספת מאוד נפוצה - הוספה של 1 למשתנה:

var++;

היא פקודה מקוצרת ל-

var += 1;

שהוא בעצמו תחביר מקוצר ל-

var = var + 1;

ובדומה, יש גם תחביר מקוצר לחיסור של 1 ממשתנה:

var--;

תקין גם לכתוב עם ++ או -- לפני המשתנה במקום אחרי:

--var;
++var;

יש הבדל מתחת למכסה המנוע, אבל הוא לא חשוב לנו. מבחינתנו, השורות האלה זהות, ואני ממליץ על התחביר הראשון (שבו ה-++ או ה--- הוא אחרי המשתנה ולא לפניו). ואגב:

אפשר להשתמש גם ב-++var או ++var או var-- או --var גם בתור ביטוי, למשל:

x = y++;
z = --w;
System.out.println(42 * (x++) / (--r));

אני לא הולך להסביר מה זה אומר, כי אני מתנגד לשימוש בזה. זה נועד לחסוך שורת קוד, אבל זה דבר שהוא לא שימושי בעיניי. גם כשיודעים מה הוא אומר, הוא לא מאוד קריא בעיניי, ולכן הוא מפר את עיקרון Code for humans FIRST, comupters SECOND (עקרון שאומר שהקוד צריך להיות כמה שיותר מובן לקריאה). אני לא רק מתנגד לשימוש בו כשאני מלמד אנשים שחדשים בתכנות, אני גם נגד שימוש שלו בעולם האמיתי. זה עניין של דעה, אבל זו דעתי.

הסיבה היחידה שאני מציין את זה בכלל היא שאולי תיתקלו בזה אם תקראו קוד של אנשים אחרים, אז שתדעו שזה דבר שקיים. ואם תיתקלו בזה, תצטרכו לבדוק על מה ההבדל בין var++ ל-++var וכל מיני כאלה. אבל ההמלצה שלי - בקוד שאתן כותבות, הימנעו משימוש ב-var++ או --var כחלק מביטוי. השמתמשו בדברים האלה רק כפקודות, כלומר:

var++;

כשורת קוד בפני עצמה - סבבה. להשתמש בזה כחלק מביטוי:

x = y++;
z = --w;
System.out.println(42 * (x++) / (--r));

את זה - לא.

נקודות עיקריות

  • הסברנו מהם משתנים
  • ראינו שב-Java צריך להצהיר על משתנים, ועל הטיפוסים שלהם
  • ראינו שטיפוסים של משתנים מוודאים שאנחנו משתמשים בדברים בצורה נכונה
  • ראינו כמה טיפוסים בJava
  • ראינו מהי השמה ואיך היא נראית ב-Java
  • ראינו תחביר מקוצר להצהרה והשמה באותה שורה
  • ראינו כמה דברים בסיסיים שאפשר לעשות עם משתנים ב-Java. בהמשך נצלול לזה לעומק יותר.

Comments