המרה אובדת בג'אווה

1. סקירה כללית

במדריך מהיר זה נדון במושג המרה אובדן ב- Java ובסיבה העומדת מאחוריו.

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

2. המרה מאובדת

המרה אובדן היא פשוט אובדן מידע בזמן הטיפול בנתונים.

בג'אווה זה תואם את האפשרות של אובדן הערך או הדיוק של המשתנה בזמן ההמרה סוג אחד למשנהו.

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

לדוגמה, בואו ננסה להקצות a ארוך ל int:

longNum ארוך = 10; int intNum = longNum;

ג'אווה תנפיק שגיאה בעת הידור קוד זה:

סוגים שאינם תואמים: המרה אובדנית אפשרית מ- long ל- int

כאן, ג'אווה תמצא ארוך ו int לא תואם וגורם לשגיאת המרה אבודה. כי יכול להיות ארוך ערכים מחוץ ל int טווח -2,147,483,648 עד 2,147,483,647.

באופן דומה, בואו ננסה להקצות a לָצוּף אל א ארוך:

float floatNum = 10.12f; longNum ארוך = floatNum;
סוגים שאינם תואמים: המרה אובדנית אפשרית מ- float ל- long

כפי ש לָצוּף יכולות להיות ערכים עשרוניים שאין להם התאמה ארוך ערך. לכן, נקבל את אותה שגיאה.

באופן דומה, הקצאת א לְהַכפִּיל מספר ל- int יגרום לאותה שגיאה:

כפול כפול = 1.2; int intNum = doubleNum;
סוגים שאינם תואמים: המרה אובדנית אפשרית מכפול ל- int

ה לְהַכפִּיל ערכים יכולים להיות גדולים מדי או קטנים מדי עבור int וערכים עשרוניים ילכו לאיבוד בהמרה. לפיכך, מדובר בהמרה אבדתית.

כמו כן, אנו יכולים להיתקל בשגיאה זו בעת ביצוע חישוב פשוט:

פרנהייט = 100; int celcius = (פרנהייט - 32) * 5.0 / 9.0;

כש לְהַכפִּיל להכפיל עם int, אנו מקבלים את התוצאה ב- לְהַכפִּיל. כתוצאה מכך, מדובר גם בהמרה אבדתית.

לכן, הסוגים שאינם תואמים ב- המרה אובדנית יכולה להיות בגדלים או סוגים שונים (מספרים שלמים או עשרוניים).

3. סוגי נתונים פרימיטיביים

בג'אווה קיימים סוגי נתונים פרימיטיביים רבים עם שיעורי העטיפה המתאימים להם.

לאחר מכן, בואו נערך רשימה שימושית של כל ההמרות האבדות האפשריות ב- Java:

  • קצר ל בתים אוֹ לְהַשְׁחִיר
  • לְהַשְׁחִיר ל בתים אוֹ קצר
  • int ל בתים, קצר אוֹ לְהַשְׁחִיר
  • ארוך ל בתים, קצר, לְהַשְׁחִיר אוֹ int
  • לָצוּף ל בתים, קצר, לְהַשְׁחִיר, int אוֹ ארוך
  • לְהַכפִּיל ל בתים, קצר, לְהַשְׁחִיר, int, ארוך אוֹ לָצוּף

שים לב שלמרות זאת קצר ו לְהַשְׁחִיר יש את אותו הגודל. עוֹד, ההמרה מ קצר ל לְהַשְׁחִיר הוא אובדן כי לְהַשְׁחִיר הוא סוג נתונים לא חתום.

4. טכניקות המרה

4.1. המרה בין סוגים פרימיטיביים

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

למשל, בואו להמיר ארוך מספר ל- a קצר באמצעות downcasting:

longNum ארוך = 24; קצר shortNum = (קצר) longNum; assertEquals (24, shortNum);

באופן דומה, בואו להמיר א לְהַכפִּיל ל int:

כפול כפול = 15.6; int integerNum = (int) doubleNum; assertEquals (15, integerNum);

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

בואו נתגייר ארוך ערכים מחוץ לטווח קצר:

ארוך גדולLongNum = 32768; shortShortNum קצר = (short) largeLongNum; assertEquals (-32768, minShortNum); ארוך smallLongNum = -32769; קצר maxShortNum = (קצר) smallLongNum; assertEquals (32767, maxShortNum);

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

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

בואו נבין זאת באמצעות דוגמאות. מתי largeLongNum עם הערך 32768 מומר ל קצר, הערך של shortNum1 הוא -32768. מכיוון שהערך המקסימלי של קצר הוא 32767, לכן Java הולכת לערך הדקות הבא של ה- קצר.

באופן דומה, מתי smallLongNum מומר ל קצר. הערך של shortNum2 הוא 32767 כאשר Java הולכת לערך המקסימלי הבא של קצר.

כמו כן, בואו נראה מה קורה כאשר אנו ממירים את ערכי המקסימום והמינימום של a ארוך ל int:

ארוך maxLong = Long.MAX_VALUE; int minInt = (int) maxLong; assertEquals (-1, minInt); ארוך minLong = ארוך.MIN_VALUE; int maxInt = (int) minLong; assertEquals (0, maxInt);

4.2. המרה בין חפצי עטיפה לסוגים פרימיטיביים

כדי להמיר ישירות אובייקט עטיפה לפרימיטיבי, אנו יכולים להשתמש בשיטות שונות בכיתות עטיפה כגון intValue (), shortValue () ו longValue (). זה נקרא ביטול איגרוף.

למשל, בואו להמיר א לָצוּף התנגדות ל ארוך:

Float floatNum = 17.564f; longNum ארוך = floatNum.longValue (); assertEquals (17, longNum);

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

ציבורי long longValue () {return (long) value; }

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

כפול כפול = 15.9999; longNum ארוך = doubleNum.longValue (); assertEquals (15, longNum); 

לאחר ההמרה, הערך של longNum יהיה בן 15. עם זאת, ה doubleNum הוא 15.9999, שזה קרוב מאוד ל -16.

במקום זאת, אנו יכולים להשתמש Math.round () להמרה למספר השלם הקרוב ביותר:

כפול כפול = 15.9999; longNum ארוך = Math.round (doubleNum); assertEquals (16, longNum);

4.3. המרה בין אובייקטים של עטיפה

לשם כך, בואו נשתמש בטכניקות ההמרה שנדונו כבר.

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

לדוגמא, בואו להמיר א לְהַכפִּיל להתנגד ל- מספר שלם לְהִתְנַגֵד:

כפול כפול = 10.3; כפול dbl = doubleNum.doubleValue (); // unboxing int intgr = (int) dbl; // downcasting Integer intNum = Integer.valueOf (intgr); assertEquals (Integer.valueOf (10), intNum); 

לבסוף, אנו משתמשים מספר שלם.ערך של() להמיר את הסוג הפרימיטיבי int ל מספר שלם לְהִתְנַגֵד. סוג זה של המרה נקרא אִגרוּף.

5. מסקנה

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

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

במקביל בחנו גם טכניקות שימושיות נוספות להמרות מספריות בג'אווה.

יישומי הקוד עבור מאמר זה ניתן למצוא באתר GitHub.